diff --git a/.gitattributes b/.gitattributes index 915d1ed51d121f1986c9dfe71cf1745c1a11286d..7d94e0961337f83b1b3f764a7e123a0a999561f8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ # Dart sources have to be normalized so that source offsets in kernel files # with source embedded in them match across all platforms. -*.dart text eol=lf \ No newline at end of file +*.dart text eol=lf +*.patch* text eol=lf \ No newline at end of file diff --git a/BUILD.gn b/BUILD.gn index 49784eff36e036769a6651ae0af76a16c39286db..50a17bd38d052768fadf424870d8aca29c4c3909 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -39,7 +39,7 @@ config("export_dynamic_symbols") { # -exported_symbols_list is the macOS linker syntax. The different flags # accept files formatted differently, so we have exported_symbols.sym for GNU # linker syntax, and exported_symbols_mac.sym for the macOS linker syntax. - if (is_linux || is_fuchsia) { + if (is_ohos|| is_linux || is_fuchsia) { inputs = [ "//flutter/common/exported_symbols.sym" ] ldflags = [ "-Wl,--dynamic-list=" + rebase_path(inputs[0], root_build_dir) ] } else if (is_mac) { @@ -129,7 +129,7 @@ group("flutter") { if (build_engine_artifacts) { public_deps += [ - "//flutter/shell/testing", + # "//flutter/shell/testing($host_toolchain)", "//flutter/tools/const_finder", "//flutter/tools/font_subset", ] @@ -174,6 +174,12 @@ group("unittests") { ] } + if (is_ohos) { + public_deps += [ + "//flutter/shell/platform/ohos:flutter_ohos_unittests", + ] + } + if (is_ios) { public_deps += [ "//flutter/shell/platform/darwin/ios:ios_test_flutter" ] } diff --git a/DEPS b/DEPS index cec3f632f4a05279325230fa3b9c856799b61394..bcb562b2c87afef9b60449ca58e2b75777a2ca20 100644 --- a/DEPS +++ b/DEPS @@ -595,7 +595,7 @@ deps = { } ], 'dep_type': 'cipd', - 'condition': 'host_os == "win" and download_dart_sdk' + 'condition': 'host_os == "win" and download_dart_sdk and not release_candidate and host_cpu == "arm64"' }, # esbuild download @@ -1256,4 +1256,10 @@ hooks = [ 'src/flutter/tools/fuchsia/test_scripts/gen_build_defs.py', ], }, + { + # Generate the ohos compile environment + 'name': 'ohos_setup', + 'pattern': 'src/flutter/attachment/scripts/.*\\.py', + 'action': ['python3', 'src/flutter/attachment/scripts/ohos_setup.py'], + } ] diff --git a/OAT.xml b/OAT.xml new file mode 100644 index 0000000000000000000000000000000000000000..a1e1a7950dadf713a2dde6ce10a619cc69c1ada6 --- /dev/null +++ b/OAT.xml @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/README.OpenSource b/README.OpenSource new file mode 100644 index 0000000000000000000000000000000000000000..d0011eeaca00e7fc9a203f1c48151d3c2b060271 --- /dev/null +++ b/README.OpenSource @@ -0,0 +1,11 @@ +[ + { + "Name": "engine", + "License": "BSD 3-Clause License", + "License File": "LICENSE", + "Version Number": "3.7.12", + "Owner": "aibin@openvalley.net", + "Upstream URL": "https://github.com/flutter/engine/", + "Description": "The Flutter Engine is a portable runtime for hosting Flutter applications. It implements Flutter's core libraries, including animation and graphics, file and network I/O, accessibility support, plugin architecture, and a Dart runtime and compile toolchain. Most developers will interact with Flutter via the Flutter Framework, which provides a modern, reactive framework, and a rich set of platform, layout and foundation widgets." + } +] \ No newline at end of file diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000000000000000000000000000000000000..97cf53ade49bb118e0f18787116a55da01370d37 --- /dev/null +++ b/README.en.md @@ -0,0 +1,142 @@ +Flutter Engine +============== + +Source of the original repository: https://github.com/flutter/engine + +## Repository Description +This repository is an extension of the Flutter engine repository. It enables Flutter engine to run on OpenHarmony devices. + +## How to Build + +* Build environment: +1. Linux or macOS that support Flutter engine; Windows that supports **gen_snapshot**. +2. Access to the **allowed_hosts** field in the DEPS file. + +* Build steps: +1. Set up a basic environment. For details, see the [official document](https://github.com/flutter/flutter/wiki/Setting-up-the-Engine-development-environment). + + The following libraries need to be installed: + + ``` + sudo apt install python3 + sudo apt install pkg-config + sudo apt install ninja-build + ``` + + For Windows: + Refer to the section "Compiling for Windows" + in the [official document](https://github.com/flutter/flutter/wiki/Compiling-the-engine#compiling-for-windows). + + +2. Configure the file. Specifically, create an empty folder **engine**, create the **.gclient** file in this folder, and edit the file. + + ``` + solutions = [ + { + "managed": False, + "name": "src/flutter", + "url": "git@gitee.com:harmonycommando_flutter/flutter_engine.git@oh-3.22.0", + "custom_deps": {}, + "deps_file": "DEPS", + "safesync_url": "", + }, + ] + ``` + +3. Synchronize the code. In the **engine** directory, execute `gclient sync`. The engine source code and packages repository will be synchronized, and the **ohos_setup** task will be executed. + +4. Download the SDK. Download the supporting development kits from [HarmonyOS SDK](https://developer.huawei.com/consumer/en/develop). Suites downloaded from other platforms are not supported. + + ```sh + # Environment variables to set: HarmonyOS SDK, ohpm, hvigor, and node. + export TOOL_HOME=/Applications/DevEco-Studio.app/Contents # macOS environment + export DEVECO_SDK_HOME=$TOOL_HOME/sdk # command-line-tools/sdk + export PATH=$TOOL_HOME/tools/ohpm/bin:$PATH # command-line-tools/ohpm/bin + export PATH=$TOOL_HOME/tools/hvigor/bin:$PATH # command-line-tools/ hvigor/bin + export PATH=$TOOL_HOME/tools/node/bin:$PATH # command-line-tools/tool/node/bin + ``` + +5. Start building. In the **engine** directory, execute `./ohos` to start building the Flutter engine that supports ohos devices. +Starting from version 3.22.0, the engine compilation will by default compile both the `local-engine` and `local-host-engine`. When the SDK specifies local compiled artifacts, it must specify both of these compiled artifacts. For example: + + ```shell + flutter build hap --target-platform ohos-arm64 --release --local-engine=/engine/src/out/ohos_release_arm64/ --local-engine-host=/engine/src/out/host_release + ``` + +6. Update the code. In the **engine** directory, execute `./ohos -b master`. + +- Manual code update method (after completing at least one full `gclient sync`): + * Navigate directly to the `engine/src/flutter` directory. After ensuring that the current code update does not involve any patches, you may freely edit or switch code branches, and then execute the build command. If patches are involved, you can manually apply the relevant patches or refer to FAQ.7 to batch reverse and reapply patches. + + +## FAQs +1. The message `Member notfound:'isOhos'` is reported during project running.
Install all dart patches in the **src/third_party/dart** directory. (The patches are located in the **src/flutter/attachment/repos** directory, and you can use **git apply** to apply the patches). Recompile the engine after installing the patches. + +2. The message `Permission denied` is reported.
Execute `chmod +x < script file >` to add the execution permission. + +3. To compile the engine in debug, release, or profile mode, execute `./ohos -t debug`, `./ohos -t release`, or `./ohos -t profile`, respectively. + +4. To find the help, execute `./ohos -h`. + +5. Different ways for handling newline characters by Windows, macOS, and Linux will cause different results of **dart vm snapshot hash** when installing the dart patches. You can obtain the value of **snapshot hash** through the following method: + + ```shell + python xxx/src/third_party/dart/tools/make_version.py --format='{{SNAPSHOT_HASH}}' + ``` + + Here, **xxx** is the engine path you created. + + If the obtained value is not **8af474944053df1f0a3be6e6165fa7cf**, check whether all lines of the **xxx/src/third_party/dart/runtime/vm/dart.cc** file and the **xxx/src/third_party/dart/runtime/vm/image_snapshot.cc** file end with **LF**. For Windows, you can use **Notepad++** to check; for other systems, consult specific methods on your own. + +6. After modifying the embedding layer code, running `./ohos` does not take effect. You need to modify the timestamp of any file in the C++ layer for the build system to recognize it (e.g., add a space in any C++ file, save it, then remove the space and save again). Re-run the build to trigger the embedding layer to be repackaged. + +7. Method to manually reapply patches: + + ```shell + python engine/src/flutter/attachment/scripts/ohos_reverse_patch.py + python engine/src/flutter/attachment/scripts/ohos_setup.py + ``` + +8. Configure automatic code navigation + + It is recommended to use the `vscode+clangd` plugin (note: it conflicts with C/C++ IntelliSense). + Example configuration: + ```json + "clangd.path": "./src/flutter/buildtools/linux-x64/clang/bin/clangd", + "clangd.arguments": [ + "--compile-commands-dir=./src/out/ohos_release_arm64/", + "--query-driver=./src/flutter/buildtools/linux-x64/clang/bin/clang++" + ], + "C_Cpp.intelliSenseEngine": "disabled", + ``` + +9. `gclient sync` error on Windows + + Key error message: `'A required privilege is not held by the client'` + + Solution: Go to Settings -> Developer Options -> Enable "Developer Mode" + + +## Code Building at the Embedding Layer + +1. Edit **shell/platform/ohos/flutter_embedding/local.properties** as follows: + + ``` + sdk.dir= + nodejs.dir= + ``` + +2. Copy the file to `shell/platform/ohos/flutter_embedding/flutter/libs/arm64-v8a/` from the built `engine` directory. + 1. For the debug or release version, copy `libflutter.so`. + 2. For the profile version, copy `libflutter.so` and `libvmservice_snapshot.so`. + +3. In the **shell/platform/ohos/flutter_embedding** directory, execute the following instruction: + + ``` + # buildMode can be set to debug, release, or profile. + hvigorw --mode module -p module=flutter@default -p product=default -p buildMode=debug assembleHar --no-daemon + ``` + +4. Find the HAR file from the path `shell/platform/ohos/flutter_embedding/flutter/build/default/outputs/default/flutter.har`. + +If you are using DevEco Studio of a Beta version and encounter the error message `must have required property 'compatibleSdkVersion', location: build-profile.json5:17:11` when building the project, refer to the section "Configuring Plugins" in chapter 6 "Creating a Project and Runing Hello World" of *DevEco Studio Environment Setup.docx* to modify the **shell/platform/ohos/flutter_embedding/hvigor/hvigor-config.json5** file. diff --git a/README.md b/README.md index 670c65921ad6cf97ed880662e6bd5f02f11244a5..d810180d87e7076ce9fe74415c60dc3a27ec7337 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,155 @@ # Flutter Engine -[![Flutter CI Status](https://flutter-dashboard.appspot.com/api/public/build-status-badge?repo=engine)](https://flutter-dashboard.appspot.com/#/build?repo=engine) -[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/flutter/engine/badge)](https://deps.dev/project/github/flutter%2Fengine) -[![SLSA 1](https://slsa.dev/images/gh-badge-level1.svg)](https://slsa.dev) - -Flutter is Google's SDK for crafting beautiful, fast user experiences for -mobile, web, and desktop from a single codebase. Flutter works with existing -code, is used by developers and organizations around the world, and is free -and open source. - -The Flutter Engine is a portable runtime for hosting -[Flutter](https://flutter.dev) applications. It implements Flutter's core -libraries, including animation and graphics, file and network I/O, -accessibility support, plugin architecture, and a Dart runtime and compile -toolchain. Most developers will interact with Flutter via the [Flutter -Framework](https://github.com/flutter/flutter), which provides a modern, -reactive framework, and a rich set of platform, layout and foundation widgets. - -If you want to run/contribute to Flutter Web engine, more tooling can be -found at [felt](https://github.com/flutter/engine/tree/main/lib/web_ui#using-felt). -This is a tool written to make web engine development experience easy. - -If you are new to Flutter, then you will find more general information -on the Flutter project, including tutorials and samples, on our Web -site at [Flutter.dev](https://flutter.dev). For specific information -about Flutter's APIs, consider our API reference which can be found at -the [docs.flutter.dev](https://docs.flutter.dev/). - -Flutter is a fully open source project, and we welcome contributions. -Information on how to get started can be found at our -[contributor guide](CONTRIBUTING.md). +原始仓来源:https://github.com/flutter/engine + +## 仓库说明: +本仓库是基于flutter官方engine仓库拓展,可构建支持在OpenHarmony设备上运行的flutter engine程序。 + +## 构建说明: + +* 构建环境: +1. 目前支持在Linux与MacOS中构建,Windows环境中支持构建gen_snapshot; +2. 请确保当前构建环境可以访问 `DEPS` 配置文件中 `allowed_hosts` 字段的URL列表。 + +* 构建步骤: +1. 构建基础环境:可参照[官网](https://github.com/flutter/flutter/wiki/Setting-up-the-Engine-development-environment); + + a) 需要安装的工具: `git`, `curl` and `unzip` + + b) 克隆 `gclient` 与 `gn` 构建工具的代码仓库 + + ``` + git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git + ``` + + 添加 `depot_tools` 到 `PATH` 环境变量中 + + ``` + export PATH=/home//depot_tools:$PATH + ``` + + c) 需要安装的基础库: + + ``` + sudo apt install python3 + sudo apt install pkg-config + sudo apt install ninja-build + ``` + + Windows构建环境: + 可参考[官网](https://github.com/flutter/flutter/wiki/Compiling-the-engine#compiling-for-windows) + "Compiling for Windows" 章节搭建Windows构建环境 + + +2. 配置文件: + + a) 创建名为 `engine` 的空目录 + + b) 在 `engine` 目录内新建 `.gclient` 文件 + + c) 编辑 `.gclient` 文件: + ``` + solutions = [ + { + "managed": False, + "name": "src/flutter", + "url": "git@gitee.com:harmonycommando_flutter/flutter_engine.git@oh-3.22.0", + "custom_deps": {}, + "deps_file": "DEPS", + "safesync_url": "", + }, + ] + ``` + +3. 同步代码:在 `engine` 目录中执行 `gclient sync` 命令;这里会同步engine源码、官方packages仓,还有执行ohos_setup任务; + +4. 下载sdk: 从[鸿蒙SDK](https://developer.huawei.com/consumer/cn/develop)下载配套开发工具,暂不支持非该渠道下载的套件 + + ```sh + # 需要设置的环境变量: HarmonyOS SDK, ohpm, hvigor, node + export TOOL_HOME=/Applications/DevEco-Studio.app/Contents # mac环境 + export DEVECO_SDK_HOME=$TOOL_HOME/sdk # command-line-tools/sdk + export PATH=$TOOL_HOME/tools/ohpm/bin:$PATH # command-line-tools/ohpm/bin + export PATH=$TOOL_HOME/tools/hvigor/bin:$PATH # command-line-tools/ hvigor/bin + export PATH=$TOOL_HOME/tools/node/bin:$PATH # command-line-tools/tool/node/bin + ``` + +5. 开始构建:在 `engine` 目录,执行`./ohos`,即可开始构建支持ohos设备的flutter engine。 + 从3.22.0版本开始,engine编译会默认编译local-engine以及local-host-engine, sdk在指定本地编译产物时需要同时指定这两个编译产物,例如: + ```shell + flutter build hap --target-platform ohos-arm64 --release --local-engine=/engine/src/out/ohos_release_arm64/ --local-engine-host=//engine/src/out/host_release + ``` + +6. 更新代码:在 `engine` 目录,执行`./ohos -b master` + +- 手动更新代码方法(至少经过一次完整gclient sync之后): + * 直接进入engine/src/flutter目录,在确保当前更新代码未涉及patch后,可以自由编辑或切换代码分支,然后执行编译命令,如若涉及patch修改,可手动apply相关patch或参考FAQ.7 批量reverse后重新apply patch + +## FAQ +1. 运行项目工程报 `Member notfound:'isOhos'` 的错误:请确保src/third_party/dart目录下应用了所有的dart patch(补丁位于src/flutter/attachment/repos目录,可使用git apply应用patch)应用patch后重新编译engine + +2. 提示`Permission denied:` 执行 `chmod +x <脚本文件>` 添加执行权限 + +3. 单独编译 `debug/release/profile` 模式的engine:`./ohos -t debug|release|profile` + +4. 查看帮助:`./ohos -h` + +5. 由于Windows和MacOS、Linux对换行符处理方式不同,在应用dart补丁时会造成dart vm snapshot hash结果不同,可通过以下方法获取当前snapshot hash值 + + ```shell + python engine/src/third_party/dart/tools/make_version.py --format='{{SNAPSHOT_HASH}}' + ``` + + 如果获取到的值不是“8af474944053df1f0a3be6e6165fa7cf”那么就需要检查 `engine/src/third_party/dart/runtime/vm/dart.cc` 文件和 `engine/src/third_party/dart/runtime/vm/image_snapshot.cc` 文件中全部行的结尾是不是以LF结尾的,Windows可以使用notepad++查看,其它系统具体方法请自行查询 + +6. 修改了embedding层代码,运行./ohos没有生效,需要将任意cpp层文件修改一下时间戳,编译系统才可以识别(例如任意cpp文件添加空格保存后再删除空格后保存),重新执行则可以触发embedding层重新打包 + +7. 手动重新应用patch的方法: + + ```shell + python engine/src/flutter/attachment/scripts/ohos_reverse_patch.py + python engine/src/flutter/attachment/scripts/ohos_setup.py + ``` + +8. 配置代码自动跳转 + + 推荐使用vscode+clangd(与C/C++ IntelliSense有冲突)插件 + 可参考配置: + ```json + "clangd.path": "./src/flutter/buildtools/linux-x64/clang/bin/clangd", + "clangd.arguments": [ + "--compile-commands-dir=./src/out/ohos_release_arm64/", + "--query-driver=./src/flutter/buildtools/linux-x64/clang/bin/clang++", + ], + "C_Cpp.intelliSenseEngine": "disabled", + ``` + +9. Windows下gclient sync报错 + + 关键报错信息'A required privilege is not held by the client' + + 解决方案:设置->开发者选项->开启"开发人员模式" + +## embedding层代码构建指导 + +1. 编辑 `shell/platform/ohos/flutter_embedding/local.properties`: + + ``` + sdk.dir= + nodejs.dir= + ``` + +2. 你需要复制文件到 `shell/platform/ohos/flutter_embedding/flutter/libs/arm64-v8a/` + 1. `debug/release`,复制 `libflutter.so` + 2. `profile`,复制 `libflutter.so` 和 `libvmservice_snapshot.so` + +3. 在 `shell/platform/ohos/flutter_embedding` 目录下,执行 + + ``` + # buildMode可选值为: debug release profile + hvigorw --mode module -p module=flutter@default -p product=default -p buildMode=debug assembleHar --no-daemon + ``` + +4. `har` 文件输出路径为:`shell/platform/ohos/flutter_embedding/flutter/build/default/outputs/default/flutter.har` + +ps:如果你使用的是DevEco Studio的Beta版本,编译工程时遇到“must have required property 'compatibleSdkVersion', location: build-profile.json5:17:11"错误,请参考《DevEco Studio环境配置指导.docx》中的‘6 创建工程和运行Hello World’【配置插件】章节修改 `shell/platform/ohos/flutter_embedding/hvigor/hvigor-config.json5` 文件。 diff --git a/assets/asset_resolver.h b/assets/asset_resolver.h index f8ec95ece6797566a12b041afc8ed75604df6da9..0b1212f414dc0395963649b719fe83861ba196a0 100644 --- a/assets/asset_resolver.h +++ b/assets/asset_resolver.h @@ -17,6 +17,7 @@ namespace flutter { class AssetManager; class APKAssetProvider; class DirectoryAssetBundle; +class OHOSAssetProvider; class AssetResolver { public: @@ -40,6 +41,9 @@ class AssetResolver { virtual const DirectoryAssetBundle* as_directory_asset_bundle() const { return nullptr; } + virtual const OHOSAssetProvider* as_ohos_asset_provider() const { + return nullptr; + } virtual bool IsValid() const = 0; diff --git a/attachment/repos/angle-3.22.patch b/attachment/repos/angle-3.22.patch new file mode 100644 index 0000000000000000000000000000000000000000..13360322e2e37ec28f82678845ebae15431df072 --- /dev/null +++ b/attachment/repos/angle-3.22.patch @@ -0,0 +1,112 @@ +diff --git a/BUILD.gn b/BUILD.gn +index 621a97bfb..7e9bb8964 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -103,7 +103,7 @@ if (angle_build_all) { + ":translator_fuzzer", + ":xxhash_fuzzer", + "$angle_root/samples:angle_samples", +- "$angle_root/src/tests:angle_tests", ++ #"$angle_root/src/tests:angle_tests", + ] + if (angle_enable_cl) { + deps += [ "$angle_root/src/libOpenCL:angle_cl" ] +@@ -559,7 +559,8 @@ angle_static_library("angle_image_util") { + sources = libangle_image_util_sources + public_configs += [ ":angle_image_util_config" ] + public_deps = [ ":angle_image_util_headers" ] +- ++ ++ angle_has_astc_encoder = false + if (angle_has_astc_encoder) { + public_deps += [ "third_party/astc-encoder:astcenc" ] + include_dirs = [ "third_party/astc-encoder/src/Source/" ] +diff --git a/gni/angle.gni b/gni/angle.gni +index 31fb784ed..8f5975ec5 100644 +--- a/gni/angle.gni ++++ b/gni/angle.gni +@@ -63,7 +63,7 @@ if (angle_has_build) { + + declare_args() { + angle_use_gbm = ozone_platform_gbm +- angle_use_x11 = ozone_platform_x11 && !is_ggp && (is_linux || is_chromeos) ++ angle_use_x11 = ozone_platform_x11 && !is_ggp && (is_linux || is_chromeos) && !is_castos + angle_use_wayland = + ozone_platform_wayland && !is_ggp && is_linux && !is_castos + angle_use_vulkan_display = (is_linux || is_chromeos) && !is_ggp +@@ -197,7 +197,9 @@ declare_args() { + + declare_args() { + # ASTC emulation is only built on standalone non-android builds +- angle_has_astc_encoder = angle_has_build && angle_standalone && !is_android ++ #angle_has_astc_encoder = angle_has_build && angle_standalone && !is_android ++ #for ohos ++ angle_has_astc_encoder = false + } + + declare_args() { +@@ -266,13 +268,13 @@ declare_args() { + angle_wayland_dir = "$angle_root/third_party/wayland" + + angle_vulkan_headers_dir = +- "$angle_root/third_party/vulkan-deps/vulkan-headers/src" ++ "//flutter/third_party/vulkan-deps/vulkan-headers/src" + angle_vulkan_loader_dir = +- "$angle_root/third_party/vulkan-deps/vulkan-loader/src" ++ "//flutter/third_party/vulkan-deps/vulkan-loader/src" + angle_vulkan_tools_dir = +- "$angle_root/third_party/vulkan-deps/vulkan-tools/src" ++ "//flutter/third_party/vulkan-deps/vulkan-tools/src" + angle_vulkan_validation_layers_dir = +- "$angle_root/third_party/vulkan-deps/vulkan-validation-layers/src" ++ "//flutter/third_party/vulkan-deps/vulkan-validation-layers/src" + + angle_build_vulkan_system_info = angle_has_build && !angle_is_winuwp + +diff --git a/include/EGL/eglplatform.h b/include/EGL/eglplatform.h +index 777e98558..23348f47a 100644 +--- a/include/EGL/eglplatform.h ++++ b/include/EGL/eglplatform.h +@@ -111,13 +111,19 @@ typedef intptr_t EGLNativeWindowType; + + #elif defined(USE_X11) + +-/* X11 (tentative) */ +-#include +-#include ++struct NativeWindow; + +-typedef Display *EGLNativeDisplayType; +-typedef Pixmap EGLNativePixmapType; +-typedef Window EGLNativeWindowType; ++typedef void* EGLNativeDisplayType; ++typedef void* EGLNativePixmapType; ++typedef struct NativeWindow* EGLNativeWindowType; ++ ++// /* X11 (tentative) */ ++// #include ++// #include ++ ++// typedef Display *EGLNativeDisplayType; ++// typedef Pixmap EGLNativePixmapType; ++// typedef Window EGLNativeWindowType; + + #elif defined(__unix__) + +diff --git a/src/common/angle_version.h b/src/common/angle_version.h +index 488c65dfe..9a0b295d9 100644 +--- a/src/common/angle_version.h ++++ b/src/common/angle_version.h +@@ -15,8 +15,12 @@ + #define ANGLE_MINOR_VERSION 1 + + #ifndef ANGLE_REVISION ++#ifndef ANGLE_COMMIT_POSITION ++# define ANGLE_REVISION 0 ++#else + # define ANGLE_REVISION ANGLE_COMMIT_POSITION + #endif ++#endif + + #define ANGLE_STRINGIFY(x) #x + #define ANGLE_MACRO_STRINGIFY(x) ANGLE_STRINGIFY(x) diff --git a/attachment/repos/angle.patch b/attachment/repos/angle.patch new file mode 100644 index 0000000000000000000000000000000000000000..e4a0952dd4dce8a288dd5aaead27e49bc803628b --- /dev/null +++ b/attachment/repos/angle.patch @@ -0,0 +1,126 @@ +diff --git a/BUILD.gn b/BUILD.gn +index efaf09033..73af49870 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -83,7 +83,7 @@ if (angle_build_all) { + ":translator_fuzzer", + ":xxhash_fuzzer", + "$angle_root/samples:angle_samples", +- "$angle_root/src/tests:angle_tests", ++ #"$angle_root/src/tests:angle_tests", + ] + if (angle_enable_cl) { + deps += [ "$angle_root/src/libOpenCL:angle_cl" ] +@@ -486,7 +486,8 @@ angle_static_library("angle_image_util") { + sources = libangle_image_util_sources + public_configs += [ ":angle_image_util_config" ] + public_deps = [ ":angle_image_util_headers" ] +- ++ ++ angle_has_astc_encoder = false + if (angle_has_astc_encoder) { + public_deps += [ "third_party/astc-encoder:astcenc" ] + include_dirs = [ "third_party/astc-encoder/src/Source/" ] +diff --git a/gni/angle.gni b/gni/angle.gni +index b8086660a..d82d056d6 100644 +--- a/gni/angle.gni ++++ b/gni/angle.gni +@@ -60,8 +60,7 @@ if (angle_has_build) { + + declare_args() { + angle_use_gbm = ozone_platform_gbm +- angle_use_x11 = +- ozone_platform_x11 && !is_ggp && (is_linux || is_chromeos) && !is_castos ++ angle_use_x11 = ozone_platform_x11 && !is_ggp && (is_linux || is_chromeos) && !is_castos + angle_use_wayland = + ozone_platform_wayland && !is_ggp && is_linux && !is_castos + angle_use_vulkan_display = (is_linux || is_chromeos) && !is_ggp +@@ -191,7 +190,9 @@ declare_args() { + + declare_args() { + # ASTC emulation is only built on standalone non-android builds +- angle_has_astc_encoder = angle_has_build && angle_standalone && !is_android ++ #angle_has_astc_encoder = angle_has_build && angle_standalone && !is_android ++ #for ohos ++ angle_has_astc_encoder = false + } + + declare_args() { +@@ -255,13 +256,13 @@ declare_args() { + angle_wayland_dir = "$angle_root/third_party/wayland" + + angle_vulkan_headers_dir = +- "$angle_root/third_party/vulkan-deps/vulkan-headers/src" ++ "//third_party/vulkan-deps/vulkan-headers/src" + angle_vulkan_loader_dir = +- "$angle_root/third_party/vulkan-deps/vulkan-loader/src" ++ "//third_party/vulkan-deps/vulkan-loader/src" + angle_vulkan_tools_dir = +- "$angle_root/third_party/vulkan-deps/vulkan-tools/src" ++ "//third_party/vulkan-deps/vulkan-tools/src" + angle_vulkan_validation_layers_dir = +- "$angle_root/third_party/vulkan-deps/vulkan-validation-layers/src" ++ "//third_party/vulkan-deps/vulkan-validation-layers/src" + + angle_build_vulkan_system_info = angle_has_build && !angle_is_winuwp + +diff --git a/include/EGL/eglplatform.h b/include/EGL/eglplatform.h +index 9ebaf00a9..fe111d115 100644 +--- a/include/EGL/eglplatform.h ++++ b/include/EGL/eglplatform.h +@@ -117,13 +117,19 @@ typedef khronos_uintptr_t EGLNativeWindowType; + + #elif defined(__unix__) || defined(USE_X11) + +-/* X11 (tentative) */ +-#include +-#include ++struct NativeWindow; + +-typedef Display *EGLNativeDisplayType; +-typedef Pixmap EGLNativePixmapType; +-typedef Window EGLNativeWindowType; ++typedef void* EGLNativeDisplayType; ++typedef void* EGLNativePixmapType; ++typedef struct NativeWindow* EGLNativeWindowType; ++ ++// /* X11 (tentative) */ ++// #include ++// #include ++ ++// typedef Display *EGLNativeDisplayType; ++// typedef Pixmap EGLNativePixmapType; ++// typedef Window EGLNativeWindowType; + + #elif defined(__APPLE__) + +diff --git a/src/common/angle_version.h b/src/common/angle_version.h +index d9d7e8929..a97e3844b 100644 +--- a/src/common/angle_version.h ++++ b/src/common/angle_version.h +@@ -14,8 +14,12 @@ + #define ANGLE_MINOR_VERSION 1 + + #ifndef ANGLE_REVISION ++#ifndef ANGLE_COMMIT_POSITION ++# define ANGLE_REVISION 0 ++#else + # define ANGLE_REVISION ANGLE_COMMIT_POSITION + #endif ++#endif + + #define ANGLE_STRINGIFY(x) #x + #define ANGLE_MACRO_STRINGIFY(x) ANGLE_STRINGIFY(x) +diff --git a/src/compiler/translator/BuildSPIRV.h b/src/compiler/translator/BuildSPIRV.h +index d67bd812b..00d22d025 100644 +--- a/src/compiler/translator/BuildSPIRV.h ++++ b/src/compiler/translator/BuildSPIRV.h +@@ -110,6 +110,8 @@ struct SpirvType + SpirvTypeSpec typeSpec; + }; + ++bool operator==(const SpirvType &a, const SpirvType &b); ++ + struct SpirvIdAndIdList + { + spirv::IdRef id; diff --git a/attachment/repos/bootstrap/ohos b/attachment/repos/bootstrap/ohos new file mode 100755 index 0000000000000000000000000000000000000000..99f75119643b6c9f0b965cccd7f0b02f5a1b79b9 --- /dev/null +++ b/attachment/repos/bootstrap/ohos @@ -0,0 +1,18 @@ +#! /bin/bash +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +# 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. +# +#编译依赖 +#sudo apt install g++-multilib git python3 curl + +python3 ./src/flutter/attachment/scripts/ohos.py $@ diff --git a/attachment/repos/bootstrap/ohos.bat b/attachment/repos/bootstrap/ohos.bat new file mode 100644 index 0000000000000000000000000000000000000000..289c3c0dab13217aaf53a5feeb1edc2377d5004c --- /dev/null +++ b/attachment/repos/bootstrap/ohos.bat @@ -0,0 +1,17 @@ +@rem Copyright (c) 2021-2023 Huawei Device Co., Ltd. +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. + +@echo off +setlocal EnableDelayedExpansion + +python3 .\src\flutter\attachment\scripts\ohos.py %* diff --git a/attachment/repos/bootstrap/setup.sh b/attachment/repos/bootstrap/setup.sh new file mode 100755 index 0000000000000000000000000000000000000000..285760b851a0d9b5cb243e9597f739d7e0c4ac0e --- /dev/null +++ b/attachment/repos/bootstrap/setup.sh @@ -0,0 +1,35 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +# 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. +#! /bin/sh + +chmod -R +w .vpython* +#cp -a .vpython* ~/ + +cp -a .vpython-root .vpython-root-new +find .vpython-root-new/ -type l -exec ls -l {} \; | awk '{ + D = ENVIRON["PWD"]; + TARGET = $9; + s = index($11, "depot"); + T = D "/" substr($11, s); + #print $9 "->" T; + cmd = "rm -f " TARGET; + system(cmd); + cmd = "ln -sf " T " " TARGET; + system(cmd); +}' + +rm -rf ~/.vpython-root +cp -a .vpython-root-new ~/.vpython-root&& rm -rf .vpython-root-new +cp -a .vpython_cipd_cache ~/ + +chmod -R +w ~/.vpython* diff --git a/attachment/repos/boringssl-3.22.patch b/attachment/repos/boringssl-3.22.patch new file mode 100644 index 0000000000000000000000000000000000000000..1b5d1e65f956d81502bfb6ece49fb58d6b8f7ac7 --- /dev/null +++ b/attachment/repos/boringssl-3.22.patch @@ -0,0 +1,21 @@ +diff --git a/BUILD.gn b/BUILD.gn +index 1ee6799..684f028 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -50,6 +50,7 @@ config("no_asm_config") { + + # This has no sources on some platforms so must be a source_set. + source_set("boringssl_asm") { ++ + visibility = [ ":*" ] # Only targets in this file can depend on this. + + defines = [] +@@ -57,7 +58,7 @@ source_set("boringssl_asm") { + include_dirs = [ "src/include" ] + asmflags = [] + +- if ((current_cpu == "arm" || current_cpu == "arm64") && is_android && ++ if ((current_cpu == "arm" || current_cpu == "arm64") && (is_android || is_ohos) && + is_clang) { + if (!bssl_use_clang_integrated_as) { + # Disable the integrated assembler and use the one shipped with the NDK. diff --git a/attachment/repos/boringssl.patch b/attachment/repos/boringssl.patch new file mode 100644 index 0000000000000000000000000000000000000000..21821bef89ffe925b5c80a150467c091dbb17a55 --- /dev/null +++ b/attachment/repos/boringssl.patch @@ -0,0 +1,39 @@ +diff --git a/BUILD.gn b/BUILD.gn +index 670224d..3f88e4e 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -49,6 +49,7 @@ config("no_asm_config") { + + # This has no sources on some platforms so must be a source_set. + source_set("boringssl_asm") { ++ + visibility = [ ":*" ] # Only targets in this file can depend on this. + + defines = [] +@@ -76,7 +77,7 @@ source_set("boringssl_asm") { + } else if (current_cpu == "x64") { + if (is_mac) { + sources += crypto_sources_mac_x86_64 +- } else if (is_linux || is_android) { ++ } else if (is_linux || is_android || is_ohos) { + sources += crypto_sources_linux_x86_64 + } else { + public_configs = [ ":no_asm_config" ] +@@ -84,14 +85,14 @@ source_set("boringssl_asm") { + } else if (current_cpu == "x86") { + if (is_mac) { + sources += crypto_sources_mac_x86 +- } else if (is_linux || is_android) { ++ } else if (is_linux || is_android || is_ohos) { + sources += crypto_sources_linux_x86 + } else { + public_configs = [ ":no_asm_config" ] + } +- } else if (current_cpu == "arm" && (is_linux || is_android)) { ++ } else if (current_cpu == "arm" && (is_linux || is_android || is_ohos)) { + sources += crypto_sources_linux_arm +- } else if (current_cpu == "arm64" && (is_linux || is_android)) { ++ } else if (current_cpu == "arm64" && (is_linux || is_android || is_ohos)) { + sources += crypto_sources_linux_aarch64 + asmflags += [ "-march=armv8-a+crypto" ] + } else { diff --git a/attachment/repos/build-3.22.patch b/attachment/repos/build-3.22.patch new file mode 100644 index 0000000000000000000000000000000000000000..c5aac94dbde07570b12fd9a9e3dc89960a85c081 --- /dev/null +++ b/attachment/repos/build-3.22.patch @@ -0,0 +1,991 @@ +diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn +index 4e62965..ea49a53 100644 +--- a/build/config/BUILD.gn ++++ b/build/config/BUILD.gn +@@ -146,6 +146,8 @@ config("default_libs") { + "dl", + "m", + ] ++ } else if (is_ohos) { ++ libs = [ "dl","hilog_ndk.z" ] + } else if (is_linux) { + libs = [ "dl" ] + } +diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn +index 87ea4c5..2dc5843 100644 +--- a/build/config/BUILDCONFIG.gn ++++ b/build/config/BUILDCONFIG.gn +@@ -194,6 +194,7 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = false + is_linux = false ++ is_ohos = false + is_mac = false + is_posix = false + is_win = true +@@ -202,6 +203,7 @@ if (current_os == "win") { + is_android = false + is_chromeos = false + is_fuchsia = false ++ is_ohos = false + is_fuchsia_host = false + is_ios = false + is_linux = false +@@ -216,6 +218,7 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = false + is_linux = false ++ is_ohos = false + is_mac = false + is_posix = true + is_win = false +@@ -227,6 +230,7 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = false + is_linux = true ++ is_ohos = false + is_mac = false + is_posix = true + is_win = false +@@ -241,6 +245,7 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = false + is_linux = false ++ is_ohos = false + is_mac = false + is_posix = true + is_win = false +@@ -252,6 +257,19 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = true + is_linux = false ++ is_ohos = false ++ is_mac = false ++ is_posix = true ++ is_win = false ++ is_wasm = false ++} else if (current_os == "ohos") { ++ is_android = false ++ is_chromeos = false ++ is_fuchsia = false ++ is_fuchsia_host = false ++ is_ios = false ++ is_linux = false ++ is_ohos = true + is_mac = false + is_posix = true + is_win = false +@@ -263,6 +281,7 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = false + is_linux = true ++ is_ohos = false + is_mac = false + is_posix = true + is_win = false +@@ -274,6 +293,7 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = false + is_linux = false ++ is_ohos = false + is_mac = false + is_posix = true + is_win = false +@@ -285,6 +305,7 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = false + is_linux = false ++ is_ohos = false + is_mac = false + is_posix = false + is_win = false +@@ -324,7 +345,7 @@ if (!is_clang && using_sanitizer) { + is_clang = true + } + +-use_flutter_cxx = is_clang && (is_linux || is_android || is_mac || is_ios) ++use_flutter_cxx = is_clang && (is_linux || is_android || is_mac || is_ios || is_ohos ) + + if (is_msan && !is_linux) { + assert(false, "Memory sanitizer is only available on Linux.") +@@ -376,7 +397,9 @@ if (is_posix) { + ] + } + +-if (is_linux) { ++if (is_ohos || (is_linux && host_os == "mac")) { ++ _native_compiler_configs += [ "//build/config/ohos:sdk" ] ++} else if (is_linux) { + _native_compiler_configs += [ "//build/config/linux:sdk" ] + } else if (is_mac) { + _native_compiler_configs += [ "//build/config/mac:sdk" ] +@@ -523,8 +546,18 @@ shlib_toolchain = false + if (custom_toolchain != "") { + assert(custom_sysroot != "") + assert(custom_target_triple != "") +- host_toolchain = "//build/toolchain/linux:clang_$host_cpu" + set_default_toolchain("//build/toolchain/custom") ++ if (host_os == "linux") { ++ if (is_clang) { ++ host_toolchain = "//build/toolchain/linux:clang_$host_cpu" ++ } else { ++ host_toolchain = "//build/toolchain/linux:$host_cpu" ++ } ++ } else if (host_os == "mac") { ++ host_toolchain = "//build/toolchain/mac:clang_$host_cpu" ++ } else { ++ assert(false, "Unknown host for ohos cross compile") ++ } + } else if (is_win) { + assert(is_clang) + host_toolchain = "//build/toolchain/win:clang_$host_cpu" +@@ -550,6 +583,19 @@ if (custom_toolchain != "") { + } else { + set_default_toolchain("//build/toolchain/android:$current_cpu") + } ++} else if (is_ohos) { ++ set_default_toolchain("//build/toolchain/custom") ++ if (host_os == "linux") { ++ if (is_clang) { ++ host_toolchain = "//build/toolchain/linux:clang_$host_cpu" ++ } else { ++ host_toolchain = "//build/toolchain/linux:$host_cpu" ++ } ++ } else if (host_os == "mac") { ++ host_toolchain = "//build/toolchain/mac:clang_$host_cpu" ++ } else { ++ assert(false, "Unknown host for ohos cross compile") ++ } + } else if (is_linux) { + if (is_clang) { + host_toolchain = "//build/toolchain/linux:clang_$host_cpu" +diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn +index 51d0b97..08b5f08 100644 +--- a/build/config/compiler/BUILD.gn ++++ b/build/config/compiler/BUILD.gn +@@ -206,10 +206,16 @@ config("compiler") { + # CPU architecture. We may or may not be doing a cross compile now, so for + # simplicity we always explicitly set the architecture. + if (current_cpu == "x64") { +- cflags += [ +- "-m64", +- "-march=x86-64", +- ] ++ if (is_ohos) { ++ cflags += [ ++ "-m64", ++ ] ++ } else { ++ cflags += [ ++ "-m64", ++ "-march=x86-64", ++ ] ++ } + ldflags += [ "-m64" ] + } else if (current_cpu == "x86") { + cflags += [ "-m32" ] +@@ -335,7 +341,7 @@ config("compiler") { + + # Linux/Android common flags setup. + # --------------------------------- +- if (is_linux || is_android) { ++ if (is_linux || is_android || is_ohos) { + cflags += [ + "-fPIC", + "-pipe", # Use pipes for communicating between sub-processes. Faster. +@@ -354,7 +360,16 @@ config("compiler") { + + # Linux-specific compiler flags setup. + # ------------------------------------ +- if (is_linux) { ++ if (is_ohos) { ++ cflags += [ "-pthread" ] ++ ldflags += [ "-pthread" ] ++ ++ if (current_cpu == "arm64") { ++ cflags += [ "--target=aarch64-linux-ohos" ] ++ ldflags += [ "--target=aarch64-linux-ohos" ] ++ cflags += [ "-DBORINGSSL_CLANG_SUPPORTS_DOT_ARCH" ] ++ } ++ } else if (is_linux) { + cflags += [ "-pthread" ] + ldflags += [ "-pthread" ] + +@@ -532,9 +547,13 @@ config("runtime_library") { + ldflags += [ "-nostdlib++" ] + } + include_dirs = [ +- "//third_party/libcxx/include", + "//third_party/libcxxabi/include", + ] ++ if (custom_toolchain != "") { ++ include_dirs += [ "$custom_toolchain/include/c++/v1" ] ++ } else { ++ include_dirs += [ "//third_party/libcxx/include" ] ++ } + } + + # Android standard library setup. +@@ -661,7 +680,7 @@ if (is_win) { + "-Wno-psabi", + ] + +- if (!is_android) { ++ if (!is_android && !is_ohos) { + default_warning_flags += [ + # Needed for nlohmann/json. + "-Wno-deprecated-literal-operator", +diff --git a/build/config/ohos/BUILD.gn b/build/config/ohos/BUILD.gn +new file mode 100644 +index 0000000..b2675d2 +--- /dev/null ++++ b/build/config/ohos/BUILD.gn +@@ -0,0 +1,29 @@ ++# Copyright (c) 2013 The Chromium 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("//build/config/ohos/config.gni") ++import("//build/config/ohos/pkg_config.gni") ++import("//build/config/features.gni") ++import("//build/config/sysroot.gni") ++import("//build/config/ui.gni") ++ ++config("sdk") { ++ if (sysroot != "") { ++ cflags = [ "--sysroot=" + sysroot ] ++ ldflags = [ "--sysroot=" + sysroot ] ++ ++ # Need to get some linker flags out of the sysroot. ++ ldflags += [ exec_script("sysroot_ld_path.py", ++ [ ++ rebase_path("//build/ohos/sysroot_ld_path.sh", ++ root_build_dir), ++ sysroot, ++ ], ++ "value") ] ++ } ++} ++ ++config("fontconfig") { ++ libs = [ ] ++} +diff --git a/build/config/ohos/config.gni b/build/config/ohos/config.gni +new file mode 100644 +index 0000000..0022d70 +--- /dev/null ++++ b/build/config/ohos/config.gni +@@ -0,0 +1,13 @@ ++# Copyright 2014 The Chromium Authors. All rights reserved. ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++ ++# This file contains common system config stuff for the Ohos build. ++ ++if (is_ohos) { ++ if (current_cpu == "arm64") { ++ ohos_app_abi = "arm64-v8a" ++ } else { ++ assert(false, "Unknown Ohos ABI: " + current_cpu) ++ } ++} +diff --git a/build/config/ohos/pkg-config.py b/build/config/ohos/pkg-config.py +new file mode 100644 +index 0000000..b4a6aff +--- /dev/null ++++ b/build/config/ohos/pkg-config.py +@@ -0,0 +1,249 @@ ++#!/usr/bin/env python3 ++# ++# Copyright (c) 2013 The Chromium 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 json ++import os ++import subprocess ++import sys ++import re ++from optparse import OptionParser ++ ++# This script runs pkg-config, optionally filtering out some results, and ++# returns the result. ++# ++# The result will be [ , , , , ] ++# where each member is itself a list of strings. ++# ++# You can filter out matches using "-v " where all results from ++# pkgconfig matching the given regular expression will be ignored. You can ++# specify more than one regular expression my specifying "-v" more than once. ++# ++# You can specify a sysroot using "-s " where sysroot is the absolute ++# system path to the sysroot used for compiling. This script will attempt to ++# generate correct paths for the sysroot. ++# ++# When using a sysroot, you must also specify the architecture via ++# "-a " where arch is either "x86" or "x64". ++# ++# CrOS systemroots place pkgconfig files at /usr/share/pkgconfig ++# and one of /usr/lib/pkgconfig or /usr/lib64/pkgconfig ++# depending on whether the systemroot is for a 32 or 64 bit architecture. They ++# specify the 'lib' or 'lib64' of the pkgconfig path by defining the ++# 'system_libdir' variable in the args.gn file. pkg_config.gni communicates this ++# variable to this script with the "--system_libdir " flag. If no ++# flag is provided, then pkgconfig files are assumed to come from ++# /usr/lib/pkgconfig. ++# ++# Additionally, you can specify the option --atleast-version. This will skip ++# the normal outputting of a dictionary and instead print true or false, ++# depending on the return value of pkg-config for the given package. ++ ++ ++def SetConfigPath(options): ++ """Set the PKG_CONFIG_LIBDIR environment variable. ++ ++ This takes into account any sysroot and architecture specification from the ++ options on the given command line. ++ """ ++ ++ sysroot = options.sysroot ++ assert sysroot ++ ++ # Compute the library path name based on the architecture. ++ arch = options.arch ++ if sysroot and not arch: ++ print("You must specify an architecture via -a if using a sysroot.") ++ sys.exit(1) ++ ++ libdir = sysroot + '/usr/' + options.system_libdir + '/pkgconfig' ++ libdir += ':' + sysroot + '/usr/share/pkgconfig' ++ os.environ['PKG_CONFIG_LIBDIR'] = libdir ++ return libdir ++ ++ ++def GetPkgConfigPrefixToStrip(options, args): ++ """Returns the prefix from pkg-config where packages are installed. ++ ++ This returned prefix is the one that should be stripped from the beginning of ++ directory names to take into account sysroots. ++ """ ++ # Some sysroots, like the Chromium OS ones, may generate paths that are not ++ # relative to the sysroot. For example, ++ # /path/to/chroot/build/x86-generic/usr/lib/pkgconfig/pkg.pc may have all ++ # paths relative to /path/to/chroot (i.e. prefix=/build/x86-generic/usr) ++ # instead of relative to /path/to/chroot/build/x86-generic (i.e prefix=/usr). ++ # To support this correctly, it's necessary to extract the prefix to strip ++ # from pkg-config's |prefix| variable. ++ prefix = subprocess.check_output([options.pkg_config, ++ "--variable=prefix"] + args, env=os.environ).decode('utf-8') ++ if prefix[-4] == '/usr': ++ return prefix[4:] ++ return prefix ++ ++ ++def MatchesAnyRegexp(flag, list_of_regexps): ++ """Returns true if the first argument matches any regular expression in the ++ given list.""" ++ for regexp in list_of_regexps: ++ if regexp.search(flag) != None: ++ return True ++ return False ++ ++ ++def RewritePath(path, strip_prefix, sysroot): ++ """Rewrites a path by stripping the prefix and prepending the sysroot.""" ++ if os.path.isabs(path) and not path.startswith(sysroot): ++ if path.startswith(strip_prefix): ++ path = path[len(strip_prefix):] ++ path = path.lstrip('/') ++ return os.path.join(sysroot, path) ++ else: ++ return path ++ ++ ++def main(): ++ # If this is run on non-Linux platforms, just return nothing and indicate ++ # success. This allows us to "kind of emulate" a Linux build from other ++ # platforms. ++ if "linux" not in sys.platform: ++ print("[[],[],[],[],[]]") ++ return 0 ++ ++ parser = OptionParser() ++ parser.add_option('-d', '--debug', action='store_true') ++ parser.add_option('-p', action='store', dest='pkg_config', type='string', ++ default='pkg-config') ++ parser.add_option('-v', action='append', dest='strip_out', type='string') ++ parser.add_option('-s', action='store', dest='sysroot', type='string') ++ parser.add_option('-a', action='store', dest='arch', type='string') ++ parser.add_option('--system_libdir', action='store', dest='system_libdir', ++ type='string', default='lib') ++ parser.add_option('--atleast-version', action='store', ++ dest='atleast_version', type='string') ++ parser.add_option('--libdir', action='store_true', dest='libdir') ++ parser.add_option('--dridriverdir', action='store_true', dest='dridriverdir') ++ parser.add_option('--version-as-components', action='store_true', ++ dest='version_as_components') ++ (options, args) = parser.parse_args() ++ ++ # Make a list of regular expressions to strip out. ++ strip_out = [] ++ if options.strip_out != None: ++ for regexp in options.strip_out: ++ strip_out.append(re.compile(regexp)) ++ ++ if options.sysroot: ++ libdir = SetConfigPath(options) ++ if options.debug: ++ sys.stderr.write('PKG_CONFIG_LIBDIR=%s\n' % libdir) ++ prefix = GetPkgConfigPrefixToStrip(options, args) ++ else: ++ prefix = '' ++ ++ if options.atleast_version: ++ # When asking for the return value, just run pkg-config and print the return ++ # value, no need to do other work. ++ if not subprocess.call([options.pkg_config, ++ "--atleast-version=" + options.atleast_version] + ++ args): ++ print("true") ++ else: ++ print("false") ++ return 0 ++ ++ if options.version_as_components: ++ cmd = [options.pkg_config, "--modversion"] + args ++ try: ++ version_string = subprocess.check_output(cmd).decode('utf-8') ++ except: ++ sys.stderr.write('Error from pkg-config.\n') ++ return 1 ++ print(json.dumps(list(map(int, version_string.strip().split("."))))) ++ return 0 ++ ++ ++ if options.libdir: ++ cmd = [options.pkg_config, "--variable=libdir"] + args ++ if options.debug: ++ sys.stderr.write('Running: %s\n' % cmd) ++ try: ++ libdir = subprocess.check_output(cmd).decode('utf-8') ++ except: ++ print("Error from pkg-config.") ++ return 1 ++ sys.stdout.write(libdir.strip()) ++ return 0 ++ ++ if options.dridriverdir: ++ cmd = [options.pkg_config, "--variable=dridriverdir"] + args ++ if options.debug: ++ sys.stderr.write('Running: %s\n' % cmd) ++ try: ++ dridriverdir = subprocess.check_output(cmd).decode('utf-8') ++ except: ++ print("Error from pkg-config.") ++ return 1 ++ sys.stdout.write(dridriverdir.strip()) ++ return ++ ++ cmd = [options.pkg_config, "--cflags", "--libs"] + args ++ if options.debug: ++ sys.stderr.write('Running: %s\n' % ' '.join(cmd)) ++ ++ try: ++ flag_string = subprocess.check_output(cmd).decode('utf-8') ++ except: ++ sys.stderr.write('Could not run pkg-config.\n') ++ return 1 ++ ++ # For now just split on spaces to get the args out. This will break if ++ # pkgconfig returns quoted things with spaces in them, but that doesn't seem ++ # to happen in practice. ++ all_flags = flag_string.strip().split(' ') ++ ++ ++ sysroot = options.sysroot ++ if not sysroot: ++ sysroot = '' ++ ++ includes = [] ++ cflags = [] ++ libs = [] ++ lib_dirs = [] ++ ++ for flag in all_flags[:]: ++ if len(flag) == 0 or MatchesAnyRegexp(flag, strip_out): ++ continue; ++ ++ if flag[:2] == '-l': ++ libs.append(RewritePath(flag[2:], prefix, sysroot)) ++ elif flag[:2] == '-L': ++ lib_dirs.append(RewritePath(flag[2:], prefix, sysroot)) ++ elif flag[:2] == '-I': ++ includes.append(RewritePath(flag[2:], prefix, sysroot)) ++ elif flag[:3] == '-Wl': ++ # Don't allow libraries to control ld flags. These should be specified ++ # only in build files. ++ pass ++ elif flag == '-pthread': ++ # Many libs specify "-pthread" which we don't need since we always include ++ # this anyway. Removing it here prevents a bunch of duplicate inclusions ++ # on the command line. ++ pass ++ else: ++ cflags.append(flag) ++ ++ # Output a GN array, the first one is the cflags, the second are the libs. The ++ # JSON formatter prints GN compatible lists when everything is a list of ++ # strings. ++ print(json.dumps([includes, cflags, libs, lib_dirs])) ++ return 0 ++ ++ ++if __name__ == '__main__': ++ sys.exit(main()) +diff --git a/build/config/ohos/pkg_config.gni b/build/config/ohos/pkg_config.gni +new file mode 100644 +index 0000000..f970ac1 +--- /dev/null ++++ b/build/config/ohos/pkg_config.gni +@@ -0,0 +1,131 @@ ++# Copyright (c) 2013 The Chromium 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("//build/config/sysroot.gni") ++ ++# Defines a config specifying the result of running pkg-config for the given ++# packages. Put the package names you want to query in the "packages" variable ++# inside the template invocation. ++# ++# You can also add defines via the "defines" variable. This can be useful to ++# add this to the config to pass defines that the library expects to get by ++# users of its headers. ++# ++# Example: ++# pkg_config("mything") { ++# packages = [ "mything1", "mything2" ] ++# defines = [ "ENABLE_AWESOME" ] ++# } ++# ++# You can also use "extra args" to filter out results (see pkg-config.py): ++# extra_args = [ "-v, "foo" ] ++# To ignore libs and ldflags (only cflags/defines will be set, which is useful ++# when doing manual dynamic linking), set: ++# ignore_libs = true ++ ++declare_args() { ++ # A pkg-config wrapper to call instead of trying to find and call the right ++ # pkg-config directly. Wrappers like this are common in cross-compilation ++ # environments. ++ # Leaving it blank defaults to searching PATH for 'pkg-config' and relying on ++ # the sysroot mechanism to find the right .pc files. ++ pkg_config = "" ++ ++ # A optional pkg-config wrapper to use for tools built on the host. ++ host_pkg_config = "" ++ ++ # CrOS systemroots place pkgconfig files at /usr/share/pkgconfig ++ # and one of /usr/lib/pkgconfig or /usr/lib64/pkgconfig ++ # depending on whether the systemroot is for a 32 or 64 bit architecture. ++ # ++ # When build under GYP, CrOS board builds specify the 'system_libdir' variable ++ # as part of the GYP_DEFINES provided by the CrOS emerge build or simple ++ # chrome build scheme. This variable permits controlling this for GN builds ++ # in similar fashion by setting the `system_libdir` variable in the build's ++ # args.gn file to 'lib' or 'lib64' as appropriate for the target architecture. ++ system_libdir = "lib" ++} ++ ++pkg_config_script = "//build/config/ohos/pkg-config.py" ++ ++# Define the args we pass to the pkg-config script for other build files that ++# need to invoke it manually. ++pkg_config_args = [] ++ ++if (sysroot != "") { ++ # Pass the sysroot if we're using one (it requires the CPU arch also). ++ pkg_config_args += [ ++ "-s", ++ rebase_path(sysroot), ++ "-a", ++ current_cpu, ++ ] ++} ++ ++if (pkg_config != "") { ++ pkg_config_args += [ ++ "-p", ++ pkg_config, ++ ] ++} ++ ++# Only use the custom libdir when building with the target sysroot. ++if (target_sysroot != "" && sysroot == target_sysroot) { ++ pkg_config_args += [ ++ "--system_libdir", ++ system_libdir, ++ ] ++} ++ ++if (host_pkg_config != "") { ++ host_pkg_config_args = [ ++ "-p", ++ host_pkg_config, ++ ] ++} else { ++ host_pkg_config_args = pkg_config_args ++} ++ ++template("pkg_config") { ++ assert(defined(invoker.packages), ++ "Variable |packages| must be defined to be a list in pkg_config.") ++ config(target_name) { ++ if (host_toolchain == current_toolchain) { ++ args = host_pkg_config_args + invoker.packages ++ } else { ++ args = pkg_config_args + invoker.packages ++ } ++ if (defined(invoker.extra_args)) { ++ args += invoker.extra_args ++ } ++ ++ pkgresult = exec_script(pkg_config_script, args, "value") ++ cflags = pkgresult[1] ++ ++ foreach(include, pkgresult[0]) { ++ if (sysroot != "") { ++ # We want the system include paths to use -isystem instead of -I to ++ # suppress warnings in those headers. ++ include_relativized = rebase_path(include, root_build_dir) ++ cflags += [ "-isystem$include_relativized" ] ++ } else { ++ cflags += [ "-I$include" ] ++ } ++ } ++ ++ if (!defined(invoker.ignore_libs) || !invoker.ignore_libs) { ++ libs = pkgresult[2] ++ lib_dirs = pkgresult[3] ++ } ++ ++ forward_variables_from(invoker, ++ [ ++ "defines", ++ "visibility", ++ ]) ++ } ++} ++ ++OHOS_NDK_ROOT = target_sysroot ++OHOS_NDK_LIB = "$OHOS_NDK_ROOT/usr/aarch64-linux-ohos" +diff --git a/build/config/ohos/sysroot_ld_path.py b/build/config/ohos/sysroot_ld_path.py +new file mode 100644 +index 0000000..2cde6e2 +--- /dev/null ++++ b/build/config/ohos/sysroot_ld_path.py +@@ -0,0 +1,21 @@ ++# Copyright (c) 2013 The Chromium Authors. All rights reserved. ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++ ++# This file takes two arguments, the relative location of the shell script that ++# does the checking, and the name of the sysroot. ++ ++# TODO(brettw) the build/linux/sysroot_ld_path.sh script should be rewritten in ++# Python in this file. ++ ++import subprocess ++import sys ++ ++if len(sys.argv) != 3: ++ print("Need two arguments") ++ sys.exit(1) ++ ++result = subprocess.check_output([sys.argv[1], sys.argv[2]], ++ universal_newlines=True).strip() ++ ++print('"%s"' % result) +diff --git a/build/config/sysroot.gni b/build/config/sysroot.gni +index 727a6b1..9f68166 100644 +--- a/build/config/sysroot.gni ++++ b/build/config/sysroot.gni +@@ -22,8 +22,10 @@ if (current_toolchain == default_toolchain && target_sysroot != "") { + } else if (is_android) { + import("//build/config/android/config.gni") + sysroot = rebase_path("$android_toolchain_root/sysroot", root_build_dir) ++} else if (is_ohos) { ++ sysroot = target_sysroot + } else if (is_linux && !is_chromeos) { +- if (use_default_linux_sysroot && !is_fuchsia) { ++ if (use_default_linux_sysroot) { + if (current_cpu == "x64") { + sysroot = + rebase_path("//build/linux/debian_sid_amd64-sysroot", root_build_dir) +diff --git a/build/ohos/ohos_create_flutter_har.py b/build/ohos/ohos_create_flutter_har.py +new file mode 100644 +index 0000000..fc46359 +--- /dev/null ++++ b/build/ohos/ohos_create_flutter_har.py +@@ -0,0 +1,133 @@ ++#!/usr/bin/env python3 ++# ++# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. ++# 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. ++ ++"""Create a HAR incorporating all the components required to build a Flutter application""" ++ ++import argparse ++import logging ++import os ++import re ++import shutil ++import subprocess ++import sys ++ ++ ++HAR_CONFIG_TEMPLATE = """ ++{ ++ "app": { ++ "signingConfigs": [], ++ "products": [ ++ { ++ "name": "default", ++ "signingConfig": "default", ++ "compileSdkVersion": "%s", ++ "compatibleSdkVersion": "%s", ++ "runtimeOS": "HarmonyOS", ++ } ++ ], ++ "buildModeSet": [ ++ { ++ "name": "debug", ++ }, ++ { ++ "name": "release" ++ }, ++ { ++ "name": "profile" ++ }, ++ ] ++ }, ++ "modules": [ ++ { ++ "name": "flutter", ++ "srcPath": "./flutter" ++ } ++ ] ++} ++""" ++ ++ ++# 更新har的配置文件,指定编译使用的api版本 ++def updateConfig(buildDir, apiInt): ++ apiStr = "4.1.0(11)" if apiInt == 11 else "5.0.0(12)" ++ jsonFile = os.path.join(buildDir, "build-profile.json5") ++ with open(jsonFile, "w", encoding="utf-8") as file: ++ file.write(HAR_CONFIG_TEMPLATE % (apiStr, apiStr)) ++ ++ ++# 执行命令 ++def runCommand(command, checkCode=True, timeout=None): ++ logging.info("runCommand start, command = %s" % (command)) ++ code = subprocess.Popen(command, shell=True).wait(timeout) ++ if code != 0: ++ logging.error("runCommand error, code = %s, command = %s" % (code, command)) ++ if checkCode: ++ exit(code) ++ else: ++ logging.info("runCommand finish, code = %s, command = %s" % (code, command)) ++ ++ ++# 编译har文件,通过hvigorw的命令行参数指定编译类型(debug/release/profile) ++def buildHar(buildDir, apiInt, buildType): ++ updateConfig(buildDir, apiInt) ++ runCommand( ++ "cd %s && hvigorw clean --mode module " % (buildDir) ++ + "-p module=flutter@default -p product=default -p buildMode=%s " % buildType ++ + "assembleHar --no-daemon" ++ ) ++ ++ ++def main(): ++ parser = argparse.ArgumentParser() ++ parser.add_argument("--embedding_src", help="Path of embedding source code.") ++ parser.add_argument("--build_dir", help="Path to build.") ++ parser.add_argument( ++ "--build_type", ++ choices=["debug", "release", "profile"], ++ help="Type to build har.", ++ ) ++ parser.add_argument("--output", help="Path to output har.") ++ parser.add_argument("--native_lib", action="append", help="Native code library.") ++ parser.add_argument("--ohos_abi", help="Native code ABI.") ++ parser.add_argument( ++ "--ohos_api_int", type=int, choices=[11, 12], help="Ohos api int." ++ ) ++ options = parser.parse_args() ++ # copy source code ++ if os.path.exists(options.build_dir): ++ shutil.rmtree(options.build_dir) ++ shutil.copytree(options.embedding_src, options.build_dir) ++ ++ # copy so files ++ for file in options.native_lib: ++ dir_name, full_file_name = os.path.split(file) ++ targetDir = os.path.join(options.build_dir, "flutter/libs", options.ohos_abi) ++ if not os.path.exists(targetDir): ++ os.makedirs(targetDir) ++ shutil.copyfile( ++ file, ++ os.path.join(targetDir, full_file_name), ++ ) ++ buildHar(options.build_dir, options.ohos_api_int, options.build_type) ++ shutil.copyfile( ++ os.path.join( ++ options.build_dir, "flutter/build/default/outputs/default/flutter.har" ++ ), ++ options.output, ++ ) ++ ++ ++if __name__ == "__main__": ++ sys.exit(main()) +diff --git a/build/ohos/sysroot_ld_path.sh b/build/ohos/sysroot_ld_path.sh +new file mode 100755 +index 0000000..4b8bf73 +--- /dev/null ++++ b/build/ohos/sysroot_ld_path.sh +@@ -0,0 +1,100 @@ ++#!/bin/sh ++# Copyright (c) 2013 The Chromium Authors. All rights reserved. ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++ ++# Reads etc/ld.so.conf and/or etc/ld.so.conf.d/*.conf and returns the ++# appropriate linker flags. ++# ++# sysroot_ld_path.sh /abspath/to/sysroot ++# ++ ++log_error_and_exit() { ++ echo $0: $@ ++ exit 1 ++} ++ ++process_entry() { ++ if [ -z "$1" ] || [ -z "$2" ]; then ++ log_error_and_exit "bad arguments to process_entry()" ++ fi ++ local root="$1" ++ local localpath="$2" ++ ++ echo $localpath | grep -qs '^/' ++ if [ $? -ne 0 ]; then ++ log_error_and_exit $localpath does not start with / ++ fi ++ local entry="$root$localpath" ++ echo -L$entry ++ echo -Wl,-rpath-link=$entry ++} ++ ++process_ld_so_conf() { ++ if [ -z "$1" ] || [ -z "$2" ]; then ++ log_error_and_exit "bad arguments to process_ld_so_conf()" ++ fi ++ local root="$1" ++ local ld_so_conf="$2" ++ ++ # ld.so.conf may include relative include paths. pushd is a bashism. ++ local saved_pwd=$(pwd) ++ cd $(dirname "$ld_so_conf") ++ ++ cat "$ld_so_conf" | \ ++ while read ENTRY; do ++ echo "$ENTRY" | grep -qs ^include ++ if [ $? -eq 0 ]; then ++ local included_files=$(echo "$ENTRY" | sed 's/^include //') ++ echo "$included_files" | grep -qs ^/ ++ if [ $? -eq 0 ]; then ++ if ls $root$included_files >/dev/null 2>&1 ; then ++ for inc_file in $root$included_files; do ++ process_ld_so_conf "$root" "$inc_file" ++ done ++ fi ++ else ++ if ls $(pwd)/$included_files >/dev/null 2>&1 ; then ++ for inc_file in $(pwd)/$included_files; do ++ process_ld_so_conf "$root" "$inc_file" ++ done ++ fi ++ fi ++ continue ++ fi ++ ++ echo "$ENTRY" | grep -qs ^/ ++ if [ $? -eq 0 ]; then ++ process_entry "$root" "$ENTRY" ++ fi ++ done ++ ++ # popd is a bashism ++ cd "$saved_pwd" ++} ++ ++# Main ++ ++if [ $# -ne 1 ]; then ++ echo Usage $0 /abspath/to/sysroot ++ exit 1 ++fi ++ ++echo $1 | grep -qs ' ' ++if [ $? -eq 0 ]; then ++ log_error_and_exit $1 contains whitespace. ++fi ++ ++LD_SO_CONF="$1/etc/ld.so.conf" ++LD_SO_CONF_D="$1/etc/ld.so.conf.d" ++ ++if [ -e "$LD_SO_CONF" ]; then ++ process_ld_so_conf "$1" "$LD_SO_CONF" | xargs echo ++elif [ -e "$LD_SO_CONF_D" ]; then ++ find "$LD_SO_CONF_D" -maxdepth 1 -name '*.conf' -print -quit > /dev/null ++ if [ $? -eq 0 ]; then ++ for entry in $LD_SO_CONF_D/*.conf; do ++ process_ld_so_conf "$1" "$entry" ++ done | xargs echo ++ fi ++fi +diff --git a/build/toolchain/custom/BUILD.gn b/build/toolchain/custom/BUILD.gn +index 3da5f93..05de4d7 100644 +--- a/build/toolchain/custom/BUILD.gn ++++ b/build/toolchain/custom/BUILD.gn +@@ -12,11 +12,11 @@ toolchain("custom") { + # these values in our scope. + cc = "${toolchain_bin}/clang" + cxx = "${toolchain_bin}/clang++" +- ar = "${toolchain_bin}/${custom_target_triple}-ar" ++ ar = "${toolchain_bin}/llvm-ar" + ld = "${toolchain_bin}/clang++" +- readelf = "${toolchain_bin}/${custom_target_triple}-readelf" +- nm = "${toolchain_bin}/${custom_target_triple}-nm" +- strip = "${toolchain_bin}/${custom_target_triple}-strip" ++ readelf = "${toolchain_bin}/llvm-readelf" ++ nm = "${toolchain_bin}/llvm-nm" ++ strip = "${toolchain_bin}/llvm-strip" + + target_triple_flags = "--target=${custom_target_triple}" + sysroot_flags = "--sysroot ${custom_sysroot}" diff --git a/attachment/repos/build-3.22.patch2 b/attachment/repos/build-3.22.patch2 new file mode 100644 index 0000000000000000000000000000000000000000..52468e1a96b965abeac3032431a1c3314e30b836 --- /dev/null +++ b/attachment/repos/build-3.22.patch2 @@ -0,0 +1,19 @@ +diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn +index 08b5f08..ac21f66 100644 +--- a/build/config/compiler/BUILD.gn ++++ b/build/config/compiler/BUILD.gn +@@ -548,12 +548,8 @@ config("runtime_library") { + } + include_dirs = [ ++ "//third_party/libcxx/include", + "//third_party/libcxxabi/include", + ] +- if (custom_toolchain != "") { +- include_dirs += [ "$custom_toolchain/include/c++/v1" ] +- } else { +- include_dirs += [ "//third_party/libcxx/include" ] +- } + } + + # Android standard library setup. +-- \ No newline at end of file diff --git a/attachment/repos/build.patch b/attachment/repos/build.patch new file mode 100644 index 0000000000000000000000000000000000000000..bbe17f9809dd6752ee533c0b15c1ec6c84574e57 --- /dev/null +++ b/attachment/repos/build.patch @@ -0,0 +1,919 @@ +diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn +index 4e62965..ea49a53 100644 +--- a/build/config/BUILD.gn ++++ b/build/config/BUILD.gn +@@ -146,6 +146,8 @@ config("default_libs") { + "dl", + "m", + ] ++ } else if (is_ohos) { ++ libs = [ "dl","hilog_ndk.z" ] + } else if (is_linux) { + libs = [ "dl" ] + } +diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn +index b8d0f47..a3d1ed9 100644 +--- a/build/config/BUILDCONFIG.gn ++++ b/build/config/BUILDCONFIG.gn +@@ -190,6 +190,7 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = false + is_linux = false ++ is_ohos = false + is_mac = false + is_posix = false + is_win = true +@@ -198,6 +199,7 @@ if (current_os == "win") { + is_android = false + is_chromeos = false + is_fuchsia = false ++ is_ohos = false + is_fuchsia_host = false + is_ios = false + is_linux = false +@@ -212,6 +214,7 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = false + is_linux = false ++ is_ohos = false + is_mac = false + is_posix = true + is_win = false +@@ -223,6 +226,7 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = false + is_linux = true ++ is_ohos = false + is_mac = false + is_posix = true + is_win = false +@@ -237,6 +241,7 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = false + is_linux = false ++ is_ohos = false + is_mac = false + is_posix = true + is_win = false +@@ -248,6 +253,19 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = true + is_linux = false ++ is_ohos = false ++ is_mac = false ++ is_posix = true ++ is_win = false ++ is_wasm = false ++} else if (current_os == "ohos") { ++ is_android = false ++ is_chromeos = false ++ is_fuchsia = false ++ is_fuchsia_host = false ++ is_ios = false ++ is_linux = false ++ is_ohos = true + is_mac = false + is_posix = true + is_win = false +@@ -259,6 +277,7 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = false + is_linux = true ++ is_ohos = false + is_mac = false + is_posix = true + is_win = false +@@ -270,6 +289,7 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = false + is_linux = false ++ is_ohos = false + is_mac = false + is_posix = true + is_win = false +@@ -281,6 +301,7 @@ if (current_os == "win") { + is_fuchsia_host = false + is_ios = false + is_linux = false ++ is_ohos = false + is_mac = false + is_posix = false + is_win = false +@@ -313,7 +334,7 @@ if (!is_clang && using_sanitizer) { + is_clang = true + } + +-use_flutter_cxx = is_clang && (is_linux || is_android || is_mac || is_ios) ++use_flutter_cxx = is_clang && (is_linux || is_android || is_mac || is_ios || is_ohos ) + + if (is_msan && !is_linux) { + assert(false, "Memory sanitizer is only available on Linux.") +@@ -365,7 +386,9 @@ if (is_posix) { + ] + } + +-if (is_linux) { ++if (is_ohos || (is_linux && host_os == "mac")) { ++ _native_compiler_configs += [ "//build/config/ohos:sdk" ] ++} else if (is_linux) { + _native_compiler_configs += [ "//build/config/linux:sdk" ] + } else if (is_mac) { + _native_compiler_configs += [ "//build/config/mac:sdk" ] +@@ -510,8 +533,18 @@ shlib_toolchain = false + if (custom_toolchain != "") { + assert(custom_sysroot != "") + assert(custom_target_triple != "") +- host_toolchain = "//build/toolchain/linux:clang_$host_cpu" + set_default_toolchain("//build/toolchain/custom") ++ if (host_os == "linux") { ++ if (is_clang) { ++ host_toolchain = "//build/toolchain/linux:clang_$host_cpu" ++ } else { ++ host_toolchain = "//build/toolchain/linux:$host_cpu" ++ } ++ } else if (host_os == "mac") { ++ host_toolchain = "//build/toolchain/mac:clang_$host_cpu" ++ } else { ++ assert(false, "Unknown host for ohos cross compile") ++ } + } else if (is_win) { + if (is_clang) { + host_toolchain = "//build/toolchain/win:clang_$host_cpu" +@@ -544,6 +577,19 @@ if (custom_toolchain != "") { + } else { + set_default_toolchain("//build/toolchain/android:$current_cpu") + } ++} else if (is_ohos) { ++ set_default_toolchain("//build/toolchain/custom") ++ if (host_os == "linux") { ++ if (is_clang) { ++ host_toolchain = "//build/toolchain/linux:clang_$host_cpu" ++ } else { ++ host_toolchain = "//build/toolchain/linux:$host_cpu" ++ } ++ } else if (host_os == "mac") { ++ host_toolchain = "//build/toolchain/mac:clang_$host_cpu" ++ } else { ++ assert(false, "Unknown host for ohos cross compile") ++ } + } else if (is_linux) { + if (is_clang) { + host_toolchain = "//build/toolchain/linux:clang_$host_cpu" +diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn +index ba20010..17e0487 100644 +--- a/build/config/compiler/BUILD.gn ++++ b/build/config/compiler/BUILD.gn +@@ -191,10 +191,16 @@ config("compiler") { + # CPU architecture. We may or may not be doing a cross compile now, so for + # simplicity we always explicitly set the architecture. + if (current_cpu == "x64") { +- cflags += [ +- "-m64", +- "-march=x86-64", +- ] ++ if (is_ohos) { ++ cflags += [ ++ "-m64", ++ ] ++ } else { ++ cflags += [ ++ "-m64", ++ "-march=x86-64", ++ ] ++ } + ldflags += [ "-m64" ] + } else if (current_cpu == "x86") { + cflags += [ "-m32" ] +@@ -315,7 +321,7 @@ config("compiler") { + + # Linux/Android common flags setup. + # --------------------------------- +- if (is_linux || is_android) { ++ if (is_linux || is_android || is_ohos) { + cflags += [ + "-fPIC", + "-pipe", # Use pipes for communicating between sub-processes. Faster. +@@ -334,7 +340,16 @@ config("compiler") { + + # Linux-specific compiler flags setup. + # ------------------------------------ +- if (is_linux) { ++ if (is_ohos) { ++ cflags += [ "-pthread" ] ++ ldflags += [ "-pthread" ] ++ ++ if (current_cpu == "arm64") { ++ cflags += [ "--target=aarch64-linux-ohos" ] ++ ldflags += [ "--target=aarch64-linux-ohos" ] ++ cflags += [ "-DBORINGSSL_CLANG_SUPPORTS_DOT_ARCH" ] ++ } ++ } else if (is_linux) { + cflags += [ "-pthread" ] + ldflags += [ "-pthread" ] + +@@ -521,9 +536,13 @@ config("runtime_library") { + ldflags += [ "-nostdlib++" ] + } + include_dirs = [ +- "//third_party/libcxx/include", + "//third_party/libcxxabi/include", + ] ++ if (custom_toolchain != "") { ++ include_dirs += [ "$custom_toolchain/include/c++/v1" ] ++ } else { ++ include_dirs += [ "//third_party/libcxx/include" ] ++ } + } + + # Android standard library setup. +@@ -860,17 +879,35 @@ config("optimize") { + cflags = [ "-Oz" ] + common_optimize_on_cflags # Favor size over speed. + } else if (is_wasm) { + cflags = [ "-Oz" ] ++ } else if (is_ohos) { ++ cflags = [ "-O3" ] + common_optimize_on_cflags + } else { + cflags = [ "-O2" ] + common_optimize_on_cflags + } + + lto_flags = [] +- if (enable_lto && (is_ios || is_android || is_fuchsia || is_wasm)) { ++ if (enable_lto && (is_ios || is_android || is_fuchsia || is_wasm || is_ohos)) { + lto_flags += [ "-flto" ] + } + ++ if (is_ohos) { ++ cflags += [ ++ "-mcpu=tsv110", ++ "-fwhole-program-vtables", ++ ] ++ } ++ + ldflags = common_optimize_on_ldflags + lto_flags + cflags += lto_flags ++ ++ if (is_ohos) { ++ ldflags += [ ++ "-Wl,--lto-O2", ++ "-Wl,--plugin-opt=-mcpu=tsv110", ++ "-Wl,-mllvm", ++ "-Wl,-wholeprogramdevirt-check=fallback", ++ ] ++ } + } + + # Turn off optimizations. +diff --git a/build/config/ohos/BUILD.gn b/build/config/ohos/BUILD.gn +new file mode 100644 +index 0000000..b2675d2 +--- /dev/null ++++ b/build/config/ohos/BUILD.gn +@@ -0,0 +1,29 @@ ++# Copyright (c) 2013 The Chromium 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("//build/config/ohos/config.gni") ++import("//build/config/ohos/pkg_config.gni") ++import("//build/config/features.gni") ++import("//build/config/sysroot.gni") ++import("//build/config/ui.gni") ++ ++config("sdk") { ++ if (sysroot != "") { ++ cflags = [ "--sysroot=" + sysroot ] ++ ldflags = [ "--sysroot=" + sysroot ] ++ ++ # Need to get some linker flags out of the sysroot. ++ ldflags += [ exec_script("sysroot_ld_path.py", ++ [ ++ rebase_path("//build/ohos/sysroot_ld_path.sh", ++ root_build_dir), ++ sysroot, ++ ], ++ "value") ] ++ } ++} ++ ++config("fontconfig") { ++ libs = [ ] ++} +diff --git a/build/config/ohos/config.gni b/build/config/ohos/config.gni +new file mode 100644 +index 0000000..0022d70 +--- /dev/null ++++ b/build/config/ohos/config.gni +@@ -0,0 +1,13 @@ ++# Copyright 2014 The Chromium Authors. All rights reserved. ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++ ++# This file contains common system config stuff for the Ohos build. ++ ++if (is_ohos) { ++ if (current_cpu == "arm64") { ++ ohos_app_abi = "arm64-v8a" ++ } else { ++ assert(false, "Unknown Ohos ABI: " + current_cpu) ++ } ++} +diff --git a/build/config/ohos/pkg-config.py b/build/config/ohos/pkg-config.py +new file mode 100644 +index 0000000..b4a6aff +--- /dev/null ++++ b/build/config/ohos/pkg-config.py +@@ -0,0 +1,249 @@ ++#!/usr/bin/env python3 ++# ++# Copyright (c) 2013 The Chromium 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 json ++import os ++import subprocess ++import sys ++import re ++from optparse import OptionParser ++ ++# This script runs pkg-config, optionally filtering out some results, and ++# returns the result. ++# ++# The result will be [ , , , , ] ++# where each member is itself a list of strings. ++# ++# You can filter out matches using "-v " where all results from ++# pkgconfig matching the given regular expression will be ignored. You can ++# specify more than one regular expression my specifying "-v" more than once. ++# ++# You can specify a sysroot using "-s " where sysroot is the absolute ++# system path to the sysroot used for compiling. This script will attempt to ++# generate correct paths for the sysroot. ++# ++# When using a sysroot, you must also specify the architecture via ++# "-a " where arch is either "x86" or "x64". ++# ++# CrOS systemroots place pkgconfig files at /usr/share/pkgconfig ++# and one of /usr/lib/pkgconfig or /usr/lib64/pkgconfig ++# depending on whether the systemroot is for a 32 or 64 bit architecture. They ++# specify the 'lib' or 'lib64' of the pkgconfig path by defining the ++# 'system_libdir' variable in the args.gn file. pkg_config.gni communicates this ++# variable to this script with the "--system_libdir " flag. If no ++# flag is provided, then pkgconfig files are assumed to come from ++# /usr/lib/pkgconfig. ++# ++# Additionally, you can specify the option --atleast-version. This will skip ++# the normal outputting of a dictionary and instead print true or false, ++# depending on the return value of pkg-config for the given package. ++ ++ ++def SetConfigPath(options): ++ """Set the PKG_CONFIG_LIBDIR environment variable. ++ ++ This takes into account any sysroot and architecture specification from the ++ options on the given command line. ++ """ ++ ++ sysroot = options.sysroot ++ assert sysroot ++ ++ # Compute the library path name based on the architecture. ++ arch = options.arch ++ if sysroot and not arch: ++ print("You must specify an architecture via -a if using a sysroot.") ++ sys.exit(1) ++ ++ libdir = sysroot + '/usr/' + options.system_libdir + '/pkgconfig' ++ libdir += ':' + sysroot + '/usr/share/pkgconfig' ++ os.environ['PKG_CONFIG_LIBDIR'] = libdir ++ return libdir ++ ++ ++def GetPkgConfigPrefixToStrip(options, args): ++ """Returns the prefix from pkg-config where packages are installed. ++ ++ This returned prefix is the one that should be stripped from the beginning of ++ directory names to take into account sysroots. ++ """ ++ # Some sysroots, like the Chromium OS ones, may generate paths that are not ++ # relative to the sysroot. For example, ++ # /path/to/chroot/build/x86-generic/usr/lib/pkgconfig/pkg.pc may have all ++ # paths relative to /path/to/chroot (i.e. prefix=/build/x86-generic/usr) ++ # instead of relative to /path/to/chroot/build/x86-generic (i.e prefix=/usr). ++ # To support this correctly, it's necessary to extract the prefix to strip ++ # from pkg-config's |prefix| variable. ++ prefix = subprocess.check_output([options.pkg_config, ++ "--variable=prefix"] + args, env=os.environ).decode('utf-8') ++ if prefix[-4] == '/usr': ++ return prefix[4:] ++ return prefix ++ ++ ++def MatchesAnyRegexp(flag, list_of_regexps): ++ """Returns true if the first argument matches any regular expression in the ++ given list.""" ++ for regexp in list_of_regexps: ++ if regexp.search(flag) != None: ++ return True ++ return False ++ ++ ++def RewritePath(path, strip_prefix, sysroot): ++ """Rewrites a path by stripping the prefix and prepending the sysroot.""" ++ if os.path.isabs(path) and not path.startswith(sysroot): ++ if path.startswith(strip_prefix): ++ path = path[len(strip_prefix):] ++ path = path.lstrip('/') ++ return os.path.join(sysroot, path) ++ else: ++ return path ++ ++ ++def main(): ++ # If this is run on non-Linux platforms, just return nothing and indicate ++ # success. This allows us to "kind of emulate" a Linux build from other ++ # platforms. ++ if "linux" not in sys.platform: ++ print("[[],[],[],[],[]]") ++ return 0 ++ ++ parser = OptionParser() ++ parser.add_option('-d', '--debug', action='store_true') ++ parser.add_option('-p', action='store', dest='pkg_config', type='string', ++ default='pkg-config') ++ parser.add_option('-v', action='append', dest='strip_out', type='string') ++ parser.add_option('-s', action='store', dest='sysroot', type='string') ++ parser.add_option('-a', action='store', dest='arch', type='string') ++ parser.add_option('--system_libdir', action='store', dest='system_libdir', ++ type='string', default='lib') ++ parser.add_option('--atleast-version', action='store', ++ dest='atleast_version', type='string') ++ parser.add_option('--libdir', action='store_true', dest='libdir') ++ parser.add_option('--dridriverdir', action='store_true', dest='dridriverdir') ++ parser.add_option('--version-as-components', action='store_true', ++ dest='version_as_components') ++ (options, args) = parser.parse_args() ++ ++ # Make a list of regular expressions to strip out. ++ strip_out = [] ++ if options.strip_out != None: ++ for regexp in options.strip_out: ++ strip_out.append(re.compile(regexp)) ++ ++ if options.sysroot: ++ libdir = SetConfigPath(options) ++ if options.debug: ++ sys.stderr.write('PKG_CONFIG_LIBDIR=%s\n' % libdir) ++ prefix = GetPkgConfigPrefixToStrip(options, args) ++ else: ++ prefix = '' ++ ++ if options.atleast_version: ++ # When asking for the return value, just run pkg-config and print the return ++ # value, no need to do other work. ++ if not subprocess.call([options.pkg_config, ++ "--atleast-version=" + options.atleast_version] + ++ args): ++ print("true") ++ else: ++ print("false") ++ return 0 ++ ++ if options.version_as_components: ++ cmd = [options.pkg_config, "--modversion"] + args ++ try: ++ version_string = subprocess.check_output(cmd).decode('utf-8') ++ except: ++ sys.stderr.write('Error from pkg-config.\n') ++ return 1 ++ print(json.dumps(list(map(int, version_string.strip().split("."))))) ++ return 0 ++ ++ ++ if options.libdir: ++ cmd = [options.pkg_config, "--variable=libdir"] + args ++ if options.debug: ++ sys.stderr.write('Running: %s\n' % cmd) ++ try: ++ libdir = subprocess.check_output(cmd).decode('utf-8') ++ except: ++ print("Error from pkg-config.") ++ return 1 ++ sys.stdout.write(libdir.strip()) ++ return 0 ++ ++ if options.dridriverdir: ++ cmd = [options.pkg_config, "--variable=dridriverdir"] + args ++ if options.debug: ++ sys.stderr.write('Running: %s\n' % cmd) ++ try: ++ dridriverdir = subprocess.check_output(cmd).decode('utf-8') ++ except: ++ print("Error from pkg-config.") ++ return 1 ++ sys.stdout.write(dridriverdir.strip()) ++ return ++ ++ cmd = [options.pkg_config, "--cflags", "--libs"] + args ++ if options.debug: ++ sys.stderr.write('Running: %s\n' % ' '.join(cmd)) ++ ++ try: ++ flag_string = subprocess.check_output(cmd).decode('utf-8') ++ except: ++ sys.stderr.write('Could not run pkg-config.\n') ++ return 1 ++ ++ # For now just split on spaces to get the args out. This will break if ++ # pkgconfig returns quoted things with spaces in them, but that doesn't seem ++ # to happen in practice. ++ all_flags = flag_string.strip().split(' ') ++ ++ ++ sysroot = options.sysroot ++ if not sysroot: ++ sysroot = '' ++ ++ includes = [] ++ cflags = [] ++ libs = [] ++ lib_dirs = [] ++ ++ for flag in all_flags[:]: ++ if len(flag) == 0 or MatchesAnyRegexp(flag, strip_out): ++ continue; ++ ++ if flag[:2] == '-l': ++ libs.append(RewritePath(flag[2:], prefix, sysroot)) ++ elif flag[:2] == '-L': ++ lib_dirs.append(RewritePath(flag[2:], prefix, sysroot)) ++ elif flag[:2] == '-I': ++ includes.append(RewritePath(flag[2:], prefix, sysroot)) ++ elif flag[:3] == '-Wl': ++ # Don't allow libraries to control ld flags. These should be specified ++ # only in build files. ++ pass ++ elif flag == '-pthread': ++ # Many libs specify "-pthread" which we don't need since we always include ++ # this anyway. Removing it here prevents a bunch of duplicate inclusions ++ # on the command line. ++ pass ++ else: ++ cflags.append(flag) ++ ++ # Output a GN array, the first one is the cflags, the second are the libs. The ++ # JSON formatter prints GN compatible lists when everything is a list of ++ # strings. ++ print(json.dumps([includes, cflags, libs, lib_dirs])) ++ return 0 ++ ++ ++if __name__ == '__main__': ++ sys.exit(main()) +diff --git a/build/config/ohos/pkg_config.gni b/build/config/ohos/pkg_config.gni +new file mode 100644 +index 0000000..f970ac1 +--- /dev/null ++++ b/build/config/ohos/pkg_config.gni +@@ -0,0 +1,131 @@ ++# Copyright (c) 2013 The Chromium 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("//build/config/sysroot.gni") ++ ++# Defines a config specifying the result of running pkg-config for the given ++# packages. Put the package names you want to query in the "packages" variable ++# inside the template invocation. ++# ++# You can also add defines via the "defines" variable. This can be useful to ++# add this to the config to pass defines that the library expects to get by ++# users of its headers. ++# ++# Example: ++# pkg_config("mything") { ++# packages = [ "mything1", "mything2" ] ++# defines = [ "ENABLE_AWESOME" ] ++# } ++# ++# You can also use "extra args" to filter out results (see pkg-config.py): ++# extra_args = [ "-v, "foo" ] ++# To ignore libs and ldflags (only cflags/defines will be set, which is useful ++# when doing manual dynamic linking), set: ++# ignore_libs = true ++ ++declare_args() { ++ # A pkg-config wrapper to call instead of trying to find and call the right ++ # pkg-config directly. Wrappers like this are common in cross-compilation ++ # environments. ++ # Leaving it blank defaults to searching PATH for 'pkg-config' and relying on ++ # the sysroot mechanism to find the right .pc files. ++ pkg_config = "" ++ ++ # A optional pkg-config wrapper to use for tools built on the host. ++ host_pkg_config = "" ++ ++ # CrOS systemroots place pkgconfig files at /usr/share/pkgconfig ++ # and one of /usr/lib/pkgconfig or /usr/lib64/pkgconfig ++ # depending on whether the systemroot is for a 32 or 64 bit architecture. ++ # ++ # When build under GYP, CrOS board builds specify the 'system_libdir' variable ++ # as part of the GYP_DEFINES provided by the CrOS emerge build or simple ++ # chrome build scheme. This variable permits controlling this for GN builds ++ # in similar fashion by setting the `system_libdir` variable in the build's ++ # args.gn file to 'lib' or 'lib64' as appropriate for the target architecture. ++ system_libdir = "lib" ++} ++ ++pkg_config_script = "//build/config/ohos/pkg-config.py" ++ ++# Define the args we pass to the pkg-config script for other build files that ++# need to invoke it manually. ++pkg_config_args = [] ++ ++if (sysroot != "") { ++ # Pass the sysroot if we're using one (it requires the CPU arch also). ++ pkg_config_args += [ ++ "-s", ++ rebase_path(sysroot), ++ "-a", ++ current_cpu, ++ ] ++} ++ ++if (pkg_config != "") { ++ pkg_config_args += [ ++ "-p", ++ pkg_config, ++ ] ++} ++ ++# Only use the custom libdir when building with the target sysroot. ++if (target_sysroot != "" && sysroot == target_sysroot) { ++ pkg_config_args += [ ++ "--system_libdir", ++ system_libdir, ++ ] ++} ++ ++if (host_pkg_config != "") { ++ host_pkg_config_args = [ ++ "-p", ++ host_pkg_config, ++ ] ++} else { ++ host_pkg_config_args = pkg_config_args ++} ++ ++template("pkg_config") { ++ assert(defined(invoker.packages), ++ "Variable |packages| must be defined to be a list in pkg_config.") ++ config(target_name) { ++ if (host_toolchain == current_toolchain) { ++ args = host_pkg_config_args + invoker.packages ++ } else { ++ args = pkg_config_args + invoker.packages ++ } ++ if (defined(invoker.extra_args)) { ++ args += invoker.extra_args ++ } ++ ++ pkgresult = exec_script(pkg_config_script, args, "value") ++ cflags = pkgresult[1] ++ ++ foreach(include, pkgresult[0]) { ++ if (sysroot != "") { ++ # We want the system include paths to use -isystem instead of -I to ++ # suppress warnings in those headers. ++ include_relativized = rebase_path(include, root_build_dir) ++ cflags += [ "-isystem$include_relativized" ] ++ } else { ++ cflags += [ "-I$include" ] ++ } ++ } ++ ++ if (!defined(invoker.ignore_libs) || !invoker.ignore_libs) { ++ libs = pkgresult[2] ++ lib_dirs = pkgresult[3] ++ } ++ ++ forward_variables_from(invoker, ++ [ ++ "defines", ++ "visibility", ++ ]) ++ } ++} ++ ++OHOS_NDK_ROOT = target_sysroot ++OHOS_NDK_LIB = "$OHOS_NDK_ROOT/usr/aarch64-linux-ohos" +diff --git a/build/config/ohos/sysroot_ld_path.py b/build/config/ohos/sysroot_ld_path.py +new file mode 100644 +index 0000000..2cde6e2 +--- /dev/null ++++ b/build/config/ohos/sysroot_ld_path.py +@@ -0,0 +1,21 @@ ++# Copyright (c) 2013 The Chromium Authors. All rights reserved. ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++ ++# This file takes two arguments, the relative location of the shell script that ++# does the checking, and the name of the sysroot. ++ ++# TODO(brettw) the build/linux/sysroot_ld_path.sh script should be rewritten in ++# Python in this file. ++ ++import subprocess ++import sys ++ ++if len(sys.argv) != 3: ++ print("Need two arguments") ++ sys.exit(1) ++ ++result = subprocess.check_output([sys.argv[1], sys.argv[2]], ++ universal_newlines=True).strip() ++ ++print('"%s"' % result) +diff --git a/build/config/sysroot.gni b/build/config/sysroot.gni +index 7987e51..cd7b060 100644 +--- a/build/config/sysroot.gni ++++ b/build/config/sysroot.gni +@@ -22,8 +22,10 @@ if (current_toolchain == default_toolchain && target_sysroot != "") { + } else if (is_android) { + import("//build/config/android/config.gni") + sysroot = rebase_path("$llvm_android_toolchain_root/sysroot") ++} else if (is_ohos) { ++ sysroot = target_sysroot + } else if (is_linux && !is_chromeos) { +- if (use_default_linux_sysroot && !is_fuchsia) { ++ if (use_default_linux_sysroot) { + if (current_cpu == "x64") { + sysroot = rebase_path("//build/linux/debian_sid_amd64-sysroot") + } else { +diff --git a/build/ohos/sysroot_ld_path.sh b/build/ohos/sysroot_ld_path.sh +new file mode 100755 +index 0000000..4b8bf73 +--- /dev/null ++++ b/build/ohos/sysroot_ld_path.sh +@@ -0,0 +1,100 @@ ++#!/bin/sh ++# Copyright (c) 2013 The Chromium Authors. All rights reserved. ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++ ++# Reads etc/ld.so.conf and/or etc/ld.so.conf.d/*.conf and returns the ++# appropriate linker flags. ++# ++# sysroot_ld_path.sh /abspath/to/sysroot ++# ++ ++log_error_and_exit() { ++ echo $0: $@ ++ exit 1 ++} ++ ++process_entry() { ++ if [ -z "$1" ] || [ -z "$2" ]; then ++ log_error_and_exit "bad arguments to process_entry()" ++ fi ++ local root="$1" ++ local localpath="$2" ++ ++ echo $localpath | grep -qs '^/' ++ if [ $? -ne 0 ]; then ++ log_error_and_exit $localpath does not start with / ++ fi ++ local entry="$root$localpath" ++ echo -L$entry ++ echo -Wl,-rpath-link=$entry ++} ++ ++process_ld_so_conf() { ++ if [ -z "$1" ] || [ -z "$2" ]; then ++ log_error_and_exit "bad arguments to process_ld_so_conf()" ++ fi ++ local root="$1" ++ local ld_so_conf="$2" ++ ++ # ld.so.conf may include relative include paths. pushd is a bashism. ++ local saved_pwd=$(pwd) ++ cd $(dirname "$ld_so_conf") ++ ++ cat "$ld_so_conf" | \ ++ while read ENTRY; do ++ echo "$ENTRY" | grep -qs ^include ++ if [ $? -eq 0 ]; then ++ local included_files=$(echo "$ENTRY" | sed 's/^include //') ++ echo "$included_files" | grep -qs ^/ ++ if [ $? -eq 0 ]; then ++ if ls $root$included_files >/dev/null 2>&1 ; then ++ for inc_file in $root$included_files; do ++ process_ld_so_conf "$root" "$inc_file" ++ done ++ fi ++ else ++ if ls $(pwd)/$included_files >/dev/null 2>&1 ; then ++ for inc_file in $(pwd)/$included_files; do ++ process_ld_so_conf "$root" "$inc_file" ++ done ++ fi ++ fi ++ continue ++ fi ++ ++ echo "$ENTRY" | grep -qs ^/ ++ if [ $? -eq 0 ]; then ++ process_entry "$root" "$ENTRY" ++ fi ++ done ++ ++ # popd is a bashism ++ cd "$saved_pwd" ++} ++ ++# Main ++ ++if [ $# -ne 1 ]; then ++ echo Usage $0 /abspath/to/sysroot ++ exit 1 ++fi ++ ++echo $1 | grep -qs ' ' ++if [ $? -eq 0 ]; then ++ log_error_and_exit $1 contains whitespace. ++fi ++ ++LD_SO_CONF="$1/etc/ld.so.conf" ++LD_SO_CONF_D="$1/etc/ld.so.conf.d" ++ ++if [ -e "$LD_SO_CONF" ]; then ++ process_ld_so_conf "$1" "$LD_SO_CONF" | xargs echo ++elif [ -e "$LD_SO_CONF_D" ]; then ++ find "$LD_SO_CONF_D" -maxdepth 1 -name '*.conf' -print -quit > /dev/null ++ if [ $? -eq 0 ]; then ++ for entry in $LD_SO_CONF_D/*.conf; do ++ process_ld_so_conf "$1" "$entry" ++ done | xargs echo ++ fi ++fi +diff --git a/build/secondary/third_party/glfw/BUILD.gn b/build/secondary/third_party/glfw/BUILD.gn +index cad6cbc..c17950b 100644 +--- a/build/secondary/third_party/glfw/BUILD.gn ++++ b/build/secondary/third_party/glfw/BUILD.gn +@@ -47,6 +47,34 @@ source_set("glfw") { + ] + + defines = [ "_GLFW_WIN32" ] ++ } else if (is_ohos) { ++ sources += [ ++ "$_checkout_dir/src/glx_context.c", ++ "$_checkout_dir/src/glx_context.h", ++ "$_checkout_dir/src/posix_time.c", ++ "$_checkout_dir/src/posix_time.h", ++ "$_checkout_dir/src/posix_thread.c", ++ "$_checkout_dir/src/posix_thread.h", ++ "$_checkout_dir/src/x11_init.c", ++ "$_checkout_dir/src/x11_monitor.c", ++ "$_checkout_dir/src/x11_platform.h", ++ "$_checkout_dir/src/x11_window.c", ++ "$_checkout_dir/src/xkb_unicode.c", ++ "$_checkout_dir/src/xkb_unicode.h", ++ ] ++ ++ defines = [ ++ "_GLFW_X11", ++ "_GLFW_HAS_XF86VM", ++ ] ++ ++ libs = [ ++ "X11", ++ "Xcursor", ++ "Xinerama", ++ "Xrandr", ++ "Xxf86vm", ++ ] + } else if (is_linux) { + sources += [ + "$_checkout_dir/src/glx_context.c", +diff --git a/build/toolchain/custom/BUILD.gn b/build/toolchain/custom/BUILD.gn +index 65b1623..a89742a 100644 +--- a/build/toolchain/custom/BUILD.gn ++++ b/build/toolchain/custom/BUILD.gn +@@ -12,11 +12,11 @@ toolchain("custom") { + # these values in our scope. + cc = "${toolchain_bin}/clang" + cxx = "${toolchain_bin}/clang++" +- ar = "${toolchain_bin}/${custom_target_triple}-ar" ++ ar = "${toolchain_bin}/llvm-ar" + ld = "${toolchain_bin}/clang++" +- readelf = "${toolchain_bin}/${custom_target_triple}-readelf" +- nm = "${toolchain_bin}/${custom_target_triple}-nm" +- strip = "${toolchain_bin}/${custom_target_triple}-strip" ++ readelf = "${toolchain_bin}/llvm-readelf" ++ nm = "${toolchain_bin}/llvm-nm" ++ strip = "${toolchain_bin}/llvm-strip" + + target_triple_flags = "--target=${custom_target_triple}" + sysroot_flags = "--sysroot ${custom_sysroot}" diff --git a/attachment/repos/dart.3.4.0.patch b/attachment/repos/dart.3.4.0.patch new file mode 100644 index 0000000000000000000000000000000000000000..332887b065fb74aa9180ff8469cc0026e4d60348 --- /dev/null +++ b/attachment/repos/dart.3.4.0.patch @@ -0,0 +1,8492 @@ +diff --git a/BUILD.gn b/BUILD.gn +index 22078180b94..e5aa4e0c8f0 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -70,7 +70,7 @@ group("runtime") { + ] + } + +- if (is_linux || is_android) { ++ if (is_linux || is_android || is_ohos) { + deps += [ "runtime/bin:abstract_socket_test" ] + } else if (is_fuchsia) { + deps += [ ":fuchsia_test_package" ] +@@ -88,7 +88,7 @@ group("runtime_precompiled") { + "runtime/bin:dart_precompiled_runtime", + "runtime/bin:process_test", + ] +- if (is_linux || is_android) { ++ if (is_linux || is_android || is_ohos) { + deps += [ "runtime/bin:abstract_socket_test" ] + } + } +diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn +index 551817d5e32..d606a4a4a14 100644 +--- a/build/config/compiler/BUILD.gn ++++ b/build/config/compiler/BUILD.gn +@@ -254,7 +254,7 @@ config("compiler") { + + # Linux/Android common flags setup. + # --------------------------------- +- if (is_linux || is_android) { ++ if (is_linux || is_android || is_ohos) { + ldflags += [ + "-Wl,-z,noexecstack", + "-Wl,-z,now", +@@ -263,7 +263,7 @@ config("compiler") { + ] + } + +- if (is_android || is_linux || is_mac || is_fuchsia) { ++ if (is_android || is_linux || is_mac || is_fuchsia || is_ohos) { + if (use_flutter_cxx) { + # shared_library_config isn't transitive, so we don't automatically get + # another versions of libcxx with and without -fPIC. Properly setting this +@@ -281,7 +281,7 @@ config("compiler") { + + # Linux-specific compiler flags setup. + # ------------------------------------ +- if (is_linux) { ++ if (is_linux || is_ohos) { + if (is_clang) { + if (dart_sysroot == "alpine") { + # alpine linux target names can be found at: +@@ -405,7 +405,7 @@ config("compiler") { + # changes since artifacts from an older version of the toolchain may or may + # not be compatible with newer ones. To achieve this, we insert a synthetic + # define into the compile line. +- if (is_clang && (is_linux || is_mac) && dart_sysroot != "alpine") { ++ if (is_clang && (is_linux || is_mac || is_ohos) && dart_sysroot != "alpine") { + if (is_linux && host_cpu == "arm64") { + toolchain_stamp_file = + "//buildtools/linux-arm64/clang/.versions/clang.cipd_version" +@@ -586,6 +586,11 @@ config("runtime_library") { + "dl", + "pthread", + ] ++ } else if (is_ohos) { ++ libs += [ ++ "dl", ++ "pthread", ++ ] + } else if (is_android) { + # Android standard library setup. + +@@ -682,7 +687,7 @@ if (is_win) { + + # The Raspberry Pi 1 toolchain enables this warning, but Dart doesn't build + # cleanly with it. +- if (is_linux && !is_clang && current_cpu == "arm" && arm_version == 6) { ++ if ((is_linux || is_ohos) && !is_clang && current_cpu == "arm" && arm_version == 6) { + default_warning_flags += [ "-Wno-type-limits" ] + } + +diff --git a/build/config/sanitizers/sanitizers.gni b/build/config/sanitizers/sanitizers.gni +index 2fcf3163f42..01eb25cb7c5 100644 +--- a/build/config/sanitizers/sanitizers.gni ++++ b/build/config/sanitizers/sanitizers.gni +@@ -3,6 +3,12 @@ + # found in the LICENSE file. + + declare_args() { ++ # Use libc++ (buildtools/third_party/libc++ and ++ # buildtools/third_party/libc++abi) instead of stdlibc++ as standard library. ++ # This is intended to be used for instrumented builds. ++ use_custom_libcxx = ++ (is_asan && (is_linux || is_ohos)) || is_lsan || is_msan || is_tsan || is_ubsan ++ + # Track where uninitialized memory originates from. From fastest to slowest: + # 0 - no tracking, 1 - track only the initial allocation site, 2 - track the + # chain of stores leading from allocation site to use site. +diff --git a/build/config/sysroot.gni b/build/config/sysroot.gni +index 2a6cab96af9..41a335fc8ec 100644 +--- a/build/config/sysroot.gni ++++ b/build/config/sysroot.gni +@@ -15,7 +15,7 @@ declare_args() { + dart_sysroot = "" + } + +-if (is_linux) { ++if (is_linux || is_ohos) { + if (dart_sysroot == "alpine") { + if (current_cpu == "x86") { + target_sysroot = +diff --git a/build/rust/rust.gni b/build/rust/rust.gni +index 0a8cdc13970..8bdbf6a1ff5 100644 +--- a/build/rust/rust.gni ++++ b/build/rust/rust.gni +@@ -27,6 +27,8 @@ template("rust_library") { + cargo_out_dir = target_out_dir + if (is_linux) { + rust_os = "unknown-linux-gnu" ++ } else if (is_ohos) { ++ rust_os = "unknown-linux-gnu" + } else if (is_mac) { + rust_os = "apple-darwin" + } else if (is_win) { +diff --git a/build/sanitizers/BUILD.gn b/build/sanitizers/BUILD.gn +index e9f8c7a55cd..b1df3aef772 100644 +--- a/build/sanitizers/BUILD.gn ++++ b/build/sanitizers/BUILD.gn +@@ -2,7 +2,7 @@ + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +-if (is_linux && !is_chromeos) { ++if ((is_linux || is_ohos) && !is_chromeos) { + # TODO(GYP): Figure out which of these work and are needed on other platforms. + copy("copy_llvm_symbolizer") { + if (is_win) { +diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart +index 1e8a0fbc5ef..0e000066c75 100644 +--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart ++++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart +@@ -772,6 +772,8 @@ final class Double implements SizedNativeType { + Abi.androidIA32: Int32(), + Abi.androidX64: Int64(), + Abi.androidRiscv64: Int64(), ++ Abi.ohosArm: Int32(), ++ Abi.ohosArm64: Int64(), + Abi.fuchsiaArm64: Int64(), + Abi.fuchsiaX64: Int64(), + Abi.fuchsiaRiscv64: Int64(), +@@ -923,12 +925,16 @@ final class Abi { + static const androidIA32 = _androidIA32; + static const linuxX64 = _linuxX64; + static const macosX64 = _macosX64; ++ static const ohosArm = _ohosArm; ++ static const ohosArm64 = _ohosArm64; + + static const _androidArm = Abi._(_Architecture.arm, _OS.android); + static const _androidArm64 = Abi._(_Architecture.arm64, _OS.android); + static const _androidIA32 = Abi._(_Architecture.ia32, _OS.android); + static const _linuxX64 = Abi._(_Architecture.x64, _OS.linux); + static const _macosX64 = Abi._(_Architecture.x64, _OS.macos); ++ static const _ohosArm = Abi._(_Architecture.arm, _OS.ohos); ++ static const _ohosArm64 = Abi._(_Architecture.arm64, _OS.ohos); + + final _OS _os; + +@@ -946,6 +952,7 @@ enum _Architecture { + + enum _OS { + android, ++ ohos, + fuchsia, + ios, + linux, +diff --git a/pkg/dds/lib/dds.dart b/pkg/dds/lib/dds.dart +index ab4de82862f..afdca00be3a 100644 +--- a/pkg/dds/lib/dds.dart ++++ b/pkg/dds/lib/dds.dart +@@ -45,7 +45,7 @@ abstract class DartDevelopmentService { + static Future startDartDevelopmentService( + Uri remoteVmServiceUri, { + Uri? serviceUri, +- bool enableAuthCodes = true, ++ bool enableAuthCodes = false, + bool ipv6 = false, + bool enableServicePortFallback = false, + List cachedUserTags = const [], +diff --git a/pkg/dds/lib/src/dap/adapters/dart.dart b/pkg/dds/lib/src/dap/adapters/dart.dart +index ae16fe10bb8..8b6fc77dedb 100644 +--- a/pkg/dds/lib/src/dap/adapters/dart.dart ++++ b/pkg/dds/lib/src/dap/adapters/dart.dart +@@ -480,7 +480,7 @@ abstract class DartDebugAdapter> output, { + this.ipv6 = false, + this.enableDds = true, +- this.enableAuthCodes = true, ++ this.enableAuthCodes = false, + this.test = false, + this.logger, + Function? onError, +diff --git a/pkg/dds/lib/src/dds_impl.dart b/pkg/dds/lib/src/dds_impl.dart +index 90608678379..5f7f46a1381 100644 +--- a/pkg/dds/lib/src/dds_impl.dart ++++ b/pkg/dds/lib/src/dds_impl.dart +@@ -72,7 +72,10 @@ class DartDevelopmentServiceImpl implements DartDevelopmentService { + _streamManager = StreamManager(this); + _packageUriConverter = PackageUriConverter(this); + _dapHandler = DapHandler(this); +- _authCode = _authCodesEnabled ? _makeAuthToken() : ''; ++ _authCode = _authCodesEnabled ? _makeAuthToken() : ''; ++ //TODO 临时关闭 ++ this._authCodesEnabled = false; ++ _authCode = "";//_authCodesEnabled ? _makeAuthToken() : ''; + } + + Future startService() async { +@@ -451,7 +454,7 @@ class DartDevelopmentServiceImpl implements DartDevelopmentService { + + @override + bool get authCodesEnabled => _authCodesEnabled; +- final bool _authCodesEnabled; ++ bool _authCodesEnabled; + String? get authCode => _authCode; + String? _authCode; + +diff --git a/pkg/vm/lib/modular/transformations/ffi/abi.dart b/pkg/vm/lib/modular/transformations/ffi/abi.dart +index 9ac7fc5d7a9..21fec27b961 100644 +--- a/pkg/vm/lib/modular/transformations/ffi/abi.dart ++++ b/pkg/vm/lib/modular/transformations/ffi/abi.dart +@@ -33,6 +33,7 @@ extension on _Architecture { + /// The operating systems the Dart VM runs on. + enum _OS { + android, ++ ohos, + fuchsia, + ios, + linux, +@@ -66,6 +67,10 @@ class Abi { + /// The application binary interface for Android on 64-bit RISC-V. + static const androidRiscv64 = _androidRiscv64; + ++ static const ohosArm = _ohosArm; ++ ++ static const ohosArm64 = _ohosArm64; ++ + /// The application binary interface for Fuchsia on the Arm64 architecture. + static const fuchsiaArm64 = _fuchsiaArm64; + +@@ -134,6 +139,8 @@ class Abi { + androidIA32, + androidX64, + androidRiscv64, ++ ohosArm, ++ ohosArm64, + fuchsiaArm64, + fuchsiaX64, + fuchsiaRiscv64, +@@ -178,6 +185,8 @@ class Abi { + static const _androidIA32 = Abi._(_Architecture.ia32, _OS.android); + static const _androidX64 = Abi._(_Architecture.x64, _OS.android); + static const _androidRiscv64 = Abi._(_Architecture.riscv64, _OS.android); ++ static const _ohosArm = Abi._(_Architecture.arm, _OS.ohos); ++ static const _ohosArm64 = Abi._(_Architecture.arm64, _OS.ohos); + static const _fuchsiaArm64 = Abi._(_Architecture.arm64, _OS.fuchsia); + static const _fuchsiaX64 = Abi._(_Architecture.x64, _OS.fuchsia); + static const _fuchsiaRiscv64 = Abi._(_Architecture.riscv64, _OS.fuchsia); +@@ -204,6 +213,8 @@ const Map abiNames = { + Abi.androidIA32: 'androidIA32', + Abi.androidX64: 'androidX64', + Abi.androidRiscv64: 'androidRiscv64', ++ Abi.ohosArm: 'ohosArm', ++ Abi.ohosArm64: 'ohosArm64', + Abi.fuchsiaArm64: 'fuchsiaArm64', + Abi.fuchsiaX64: 'fuchsiaX64', + Abi.fuchsiaRiscv64: 'fuchsiaRiscv64', +@@ -245,6 +256,7 @@ final Map wordSize = + const Map> nonSizeAlignment = { + // _wordSize64 + Abi.androidArm64: _wordSize64, ++ Abi.ohosArm64: _wordSize64, + Abi.androidX64: _wordSize64, + Abi.androidRiscv64: _wordSize64, + Abi.fuchsiaArm64: _wordSize64, +@@ -265,6 +277,7 @@ const Map> nonSizeAlignment = { + Abi.linuxIA32: _wordSize32Align32, + // _wordSize32Align64 + Abi.androidArm: _wordSize32Align64, ++ Abi.ohosArm: _wordSize32Align64, + Abi.linuxArm: _wordSize32Align64, + Abi.linuxRiscv32: _wordSize32Align64, + Abi.windowsIA32: _wordSize32Align64, +diff --git a/pkg/vm/lib/target_os.dart b/pkg/vm/lib/target_os.dart +index 741c090aba4..d214e3d6991 100644 +--- a/pkg/vm/lib/target_os.dart ++++ b/pkg/vm/lib/target_os.dart +@@ -7,6 +7,7 @@ enum TargetOS { + fuchsia('fuchsia', '/'), + iOS('ios', '/'), + linux('linux', '/'), ++ ohos('ohos', '/'), + macOS('macos', '/'), + windows('windows', '\\'); + +diff --git a/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart b/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart +index 6d4e7b0871e..436f3d03301 100644 +--- a/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart ++++ b/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart +@@ -10,6 +10,8 @@ import 'dart:ffi'; + Abi.androidIA32: Uint32(), + Abi.androidX64: Uint32(), + Abi.androidRiscv64: Int32(), ++ Abi.ohosArm: Uint32(), ++ Abi.ohosArm64: Uint32(), + Abi.fuchsiaArm64: Uint32(), + Abi.fuchsiaX64: Uint32(), + Abi.fuchsiaRiscv64: Uint32(), +diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn +index c2920b70289..f0f004534df 100644 +--- a/runtime/BUILD.gn ++++ b/runtime/BUILD.gn +@@ -94,7 +94,7 @@ config("dart_precompiler_config") { + + config("dart_os_config") { + defines = [] +- ++ print("DART TARGET OS: "+target_os ) + if (target_os == "android") { + defines += [ "DART_TARGET_OS_ANDROID" ] + } else if (target_os == "fuchsia") { +@@ -104,6 +104,11 @@ config("dart_os_config") { + defines += [ "DART_TARGET_OS_MACOS_IOS" ] + } else if (target_os == "linux") { + defines += [ "DART_TARGET_OS_LINUX" ] ++ } else if (target_os == "ohos") { ++ defines += [ "DART_TARGET_OS_OHOS" ] ++ if (is_ohos) { ++ defines += [ "DART_RUNTIME_OS_OHOS" ] ++ } + } else if (target_os == "mac") { + defines += [ "DART_TARGET_OS_MACOS" ] + } else if (target_os == "win") { +@@ -221,6 +226,7 @@ config("dart_config") { + "-ggdb3", + "-fno-rtti", + "-fno-exceptions", ++ "-Wno-sign-compare" + ] + if (is_clang) { + cflags += [ +@@ -249,6 +255,11 @@ config("dart_config") { + cflags += [ "-O${dart_default_optimization_level}" ] + } + ++ ++ if (is_ohos) { ++ ldflags += ["-lhilog_ndk.z"] ++ } ++ + if (is_fuchsia) { + # safe-stack does not work with the interpreter. + cflags += [ +diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn +index 52b9bdba29e..08547c0029f 100644 +--- a/runtime/bin/BUILD.gn ++++ b/runtime/bin/BUILD.gn +@@ -28,6 +28,16 @@ config("libdart_builtin_config") { + "log", + ] + } ++ ++ if (is_linux && is_ohos) { ++ is_linux = false ++ } ++ ++ if(is_ohos && !is_win){ ++ libs +=[ ++ "hilog_ndk.z" ++ ] ++ } + } + + config("export_api_symbols") { +@@ -450,7 +460,7 @@ template("dart_io") { + ] + } + +- if (is_linux || is_win || is_fuchsia) { ++ if (is_linux || is_win || is_fuchsia || is_ohos) { + if (dart_use_fallback_root_certificates) { + deps += [ "../../third_party/fallback_root_certificates" ] + } else { +@@ -1007,6 +1017,15 @@ shared_library("entrypoints_verification_test") { + # The only effect of DART_SHARED_LIB is to export the Dart API. + "DART_SHARED_LIB", + ] ++ if (is_linux || is_android || (is_ohos || !is_win)) { ++ cflags = [ "-fPIC" ] ++ } ++ if (is_win) { ++ # TODO(dartbug.com/40579): This wrongly links in dart.exe on precompiled. ++ libs = [ "dart.lib" ] ++ abs_root_out_dir = rebase_path(root_out_dir) ++ ldflags = [ "/LIBPATH:$abs_root_out_dir" ] ++ } + } + + shared_library("ffi_test_dynamic_library") { +@@ -1017,6 +1036,9 @@ shared_library("ffi_test_dynamic_library") { + # The only effect of DART_SHARED_LIB is to export the Dart API. + "DART_SHARED_LIB", + ] ++ if (is_linux || is_android || is_ohos) { ++ cflags = [ "-fPIC" ] ++ } + } + + shared_library("ffi_test_functions") { +@@ -1045,6 +1067,9 @@ shared_library("ffi_test_functions") { + # The only effect of DART_SHARED_LIB is to export the Dart API. + "DART_SHARED_LIB", + ] ++ if (is_linux || is_android || is_ohos) { ++ cflags = [ "-fPIC" ] ++ } + if (is_win) { + # TODO(dartbug.com/40579): This wrongly links in dart.exe on precompiled. + libs = [ "dart.lib" ] +diff --git a/runtime/bin/abstract_socket_test.cc b/runtime/bin/abstract_socket_test.cc +index f9841b8655e..153f966ba3d 100644 +--- a/runtime/bin/abstract_socket_test.cc ++++ b/runtime/bin/abstract_socket_test.cc +@@ -8,7 +8,7 @@ + // closes the connection and UNIX socket. + + #include "platform/globals.h" +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + + #include + #include +@@ -101,4 +101,4 @@ int main() { + return -1; + } + +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/address_sanitizer.cc b/runtime/bin/address_sanitizer.cc +index 4b8776df7a5..5f3b9ce4f2f 100644 +--- a/runtime/bin/address_sanitizer.cc ++++ b/runtime/bin/address_sanitizer.cc +@@ -4,7 +4,7 @@ + + #include "platform/globals.h" + +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOSX) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOSX) || defined(DART_HOST_OS_OHOS) + #if defined(__has_feature) + #if __has_feature(address_sanitizer) + #if !defined(GOOGLE3) +@@ -25,4 +25,4 @@ __asan_default_options() { + #endif // !defined(GOOGLE3) + #endif // __has_feature(address_sanitizer) + #endif // defined(__has_feature) +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOSX) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOSX) || defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/analyze_snapshot.cc b/runtime/bin/analyze_snapshot.cc +index 6c38f9e589a..6320d28dbb7 100644 +--- a/runtime/bin/analyze_snapshot.cc ++++ b/runtime/bin/analyze_snapshot.cc +@@ -10,7 +10,7 @@ + #include "bin/platform.h" + + #if defined(TARGET_ARCH_IS_64_BIT) && defined(DART_PRECOMPILED_RUNTIME) && \ +- (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX)) ++ (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_OHOS)) + #define SUPPORT_ANALYZE_SNAPSHOT + #endif + +diff --git a/runtime/bin/builtin_impl_sources.gni b/runtime/bin/builtin_impl_sources.gni +index 0db1b7655bc..0a3a7e4d2ed 100644 +--- a/runtime/bin/builtin_impl_sources.gni ++++ b/runtime/bin/builtin_impl_sources.gni +@@ -13,12 +13,14 @@ builtin_impl_sources = [ + "crypto_linux.cc", + "crypto_macos.cc", + "crypto_win.cc", ++ "crypto_ohos.cc", + "dartutils.cc", + "dartutils.h", + "directory.cc", + "directory.h", + "directory_fuchsia.cc", + "directory_linux.cc", ++ "directory_ohos.cc", + "directory_macos.cc", + "directory_win.cc", + "exe_utils.cc", +@@ -27,10 +29,12 @@ builtin_impl_sources = [ + "fdutils_fuchsia.cc", + "fdutils_linux.cc", + "fdutils_macos.cc", ++ "fdutils_ohos.cc", + "file.cc", + "file.h", + "file_fuchsia.cc", + "file_linux.cc", ++ "file_ohos.cc", + "file_macos.cc", + "file_support.cc", + "file_win.cc", +@@ -51,9 +55,12 @@ builtin_impl_sources = [ + "thread_macos.h", + "thread_win.cc", + "thread_win.h", ++ "thread_ohos.cc", ++ "thread_ohos.h", + "utils.cc", + "utils.h", + "utils_fuchsia.cc", ++ "utils_ohos.cc", + "utils_linux.cc", + "utils_macos.cc", + "utils_win.cc", +diff --git a/runtime/bin/builtin_natives.cc b/runtime/bin/builtin_natives.cc +index bd28a128a22..f5b2cb89618 100644 +--- a/runtime/bin/builtin_natives.cc ++++ b/runtime/bin/builtin_natives.cc +@@ -18,6 +18,11 @@ + #include "bin/io_natives.h" + #include "bin/platform.h" + ++#ifdef DART_HOST_OS_OHOS ++#include ++#include ++#endif ++ + namespace dart { + namespace bin { + +@@ -108,6 +113,13 @@ void FUNCTION_NAME(Builtin_PrintString)(Dart_NativeArguments args) { + Dart_ServiceSendDataEvent("Stdout", "WriteEvent", chars, new_length); + ASSERT(res == nullptr); + } ++ ++#ifdef DART_HOST_OS_OHOS ++ { ++ pthread_t thread = pthread_self(); ++ OH_LOG_Print(LOG_APP,LOG_INFO,LOG_DOMAIN,"XComDartVm","Thread:%{public}lu %{public}s",thread,chars); ++ } ++#endif + } + + } // namespace bin +diff --git a/runtime/bin/console_posix.cc b/runtime/bin/console_posix.cc +index f318ae1ef6a..59c2b6c6509 100644 +--- a/runtime/bin/console_posix.cc ++++ b/runtime/bin/console_posix.cc +@@ -4,7 +4,7 @@ + + #include "platform/globals.h" + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + + #include "bin/console.h" + +@@ -28,4 +28,4 @@ void Console::RestoreConfig() { + } // namespace dart + + #endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/crypto_ohos.cc b/runtime/bin/crypto_ohos.cc +new file mode 100644 +index 00000000000..b7aca695fb7 +--- /dev/null ++++ b/runtime/bin/crypto_ohos.cc +@@ -0,0 +1,44 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/crypto.h" ++#include "bin/fdutils.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool Crypto::GetRandomBytes(intptr_t count, uint8_t* buffer) { ++ ThreadSignalBlocker signal_blocker(SIGPROF); ++ intptr_t fd = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ open("/dev/urandom", O_RDONLY | O_CLOEXEC)); ++ if (fd < 0) { ++ return false; ++ } ++ intptr_t bytes_read = 0; ++ do { ++ int res = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ read(fd, buffer + bytes_read, count - bytes_read)); ++ if (res < 0) { ++ int err = errno; ++ close(fd); ++ errno = err; ++ return false; ++ } ++ bytes_read += res; ++ } while (bytes_read < count); ++ close(fd); ++ return true; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/directory_ohos.cc b/runtime/bin/directory_ohos.cc +new file mode 100644 +index 00000000000..66363324364 +--- /dev/null ++++ b/runtime/bin/directory_ohos.cc +@@ -0,0 +1,513 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/directory.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/crypto.h" ++#include "bin/dartutils.h" ++#include "bin/fdutils.h" ++#include "bin/file.h" ++#include "bin/namespace.h" ++#include "bin/platform.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++PathBuffer::PathBuffer() : length_(0) { ++ data_ = calloc(PATH_MAX + 1, sizeof(char)); // NOLINT ++} ++ ++PathBuffer::~PathBuffer() { ++ free(data_); ++} ++ ++bool PathBuffer::AddW(const wchar_t* name) { ++ UNREACHABLE(); ++ return false; ++} ++ ++char* PathBuffer::AsString() const { ++ return reinterpret_cast(data_); ++} ++ ++wchar_t* PathBuffer::AsStringW() const { ++ UNREACHABLE(); ++ return NULL; ++} ++ ++const char* PathBuffer::AsScopedString() const { ++ return DartUtils::ScopedCopyCString(AsString()); ++} ++ ++bool PathBuffer::Add(const char* name) { ++ char* data = AsString(); ++ int written = snprintf(data + length_, PATH_MAX - length_, "%s", name); ++ data[PATH_MAX] = '\0'; ++ if ((written <= PATH_MAX - length_) && (written >= 0) && ++ (static_cast(written) == strnlen(name, PATH_MAX + 1))) { ++ length_ += written; ++ return true; ++ } else { ++ errno = ENAMETOOLONG; ++ return false; ++ } ++} ++ ++void PathBuffer::Reset(intptr_t new_length) { ++ length_ = new_length; ++ AsString()[length_] = '\0'; ++} ++ ++// A linked list of symbolic links, with their unique file system identifiers. ++// These are scanned to detect loops while doing a recursive directory listing. ++struct LinkList { ++ dev_t dev; ++ ino64_t ino; ++ LinkList* next; ++}; ++ ++ListType DirectoryListingEntry::Next(DirectoryListing* listing) { ++ if (done_) { ++ return kListDone; ++ } ++ ++ if (fd_ == -1) { ++ ASSERT(lister_ == 0); ++ NamespaceScope ns(listing->namespc(), listing->path_buffer().AsString()); ++ const int listingfd = ++ TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_DIRECTORY)); ++ if (listingfd < 0) { ++ done_ = true; ++ return kListError; ++ } ++ fd_ = listingfd; ++ } ++ ++ if (lister_ == 0) { ++ do { ++ lister_ = reinterpret_cast(fdopendir(fd_)); ++ } while ((lister_ == 0) && (errno == EINTR)); ++ if (lister_ == 0) { ++ done_ = true; ++ return kListError; ++ } ++ if (parent_ != NULL) { ++ if (!listing->path_buffer().Add(File::PathSeparator())) { ++ return kListError; ++ } ++ } ++ path_length_ = listing->path_buffer().length(); ++ } ++ // Reset. ++ listing->path_buffer().Reset(path_length_); ++ ResetLink(); ++ ++ // Iterate the directory and post the directories and files to the ++ // ports. ++ errno = 0; ++ dirent* entry = readdir(reinterpret_cast(lister_)); ++ if (entry != NULL) { ++ if (!listing->path_buffer().Add(entry->d_name)) { ++ done_ = true; ++ return kListError; ++ } ++ switch (entry->d_type) { ++ case DT_DIR: ++ if ((strcmp(entry->d_name, ".") == 0) || ++ (strcmp(entry->d_name, "..") == 0)) { ++ return Next(listing); ++ } ++ return kListDirectory; ++ case DT_BLK: ++ case DT_CHR: ++ case DT_FIFO: ++ case DT_SOCK: ++ case DT_REG: ++ return kListFile; ++ case DT_LNK: ++ if (!listing->follow_links()) { ++ return kListLink; ++ } ++ // Else fall through to next case. ++ FALL_THROUGH; ++ case DT_UNKNOWN: { ++ // On some file systems the entry type is not determined by ++ // readdir. For those and for links we use stat to determine ++ // the actual entry type. Notice that stat returns the type of ++ // the file pointed to. ++ NamespaceScope ns(listing->namespc(), ++ listing->path_buffer().AsString()); ++ struct stat64 entry_info; ++ int stat_success; ++ stat_success = TEMP_FAILURE_RETRY( ++ fstatat64(ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW)); ++ if (stat_success == -1) { ++ return kListError; ++ } ++ if (listing->follow_links() && S_ISLNK(entry_info.st_mode)) { ++ // Check to see if we are in a loop created by a symbolic link. ++ LinkList current_link = {entry_info.st_dev, entry_info.st_ino, link_}; ++ LinkList* previous = link_; ++ while (previous != NULL) { ++ if ((previous->dev == current_link.dev) && ++ (previous->ino == current_link.ino)) { ++ // Report the looping link as a link, rather than following it. ++ return kListLink; ++ } ++ previous = previous->next; ++ } ++ stat_success = ++ TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0)); ++ if (stat_success == -1 || (S_IFMT & entry_info.st_mode) == 0) { ++ // Report a broken link as a link, even if follow_links is true. ++ // A symbolic link can potentially point to an anon_inode. For ++ // example, an epoll file descriptor will have a symbolic link whose ++ // content is the string anon_inode:[eventpoll]. In this case, the ++ // target doesn't belong to any regular file catogory. ++ return kListLink; ++ } ++ if (S_ISDIR(entry_info.st_mode)) { ++ // Recurse into the subdirectory with current_link added to the ++ // linked list of seen file system links. ++ link_ = new LinkList(current_link); ++ if ((strcmp(entry->d_name, ".") == 0) || ++ (strcmp(entry->d_name, "..") == 0)) { ++ return Next(listing); ++ } ++ return kListDirectory; ++ } ++ } ++ if (S_ISDIR(entry_info.st_mode)) { ++ if ((strcmp(entry->d_name, ".") == 0) || ++ (strcmp(entry->d_name, "..") == 0)) { ++ return Next(listing); ++ } ++ return kListDirectory; ++ } else if (S_ISLNK(entry_info.st_mode)) { ++ return kListLink; ++ } else { ++ // Regular files, character devices, block devices, fifos, sockets and ++ // unknown types are all considered as files. ++ return kListFile; ++ } ++ } ++ ++ default: ++ // We should have covered all the bases. If not, let's get an error. ++ FATAL("Unexpected d_type: %d\n", entry->d_type); ++ return kListError; ++ } ++ } ++ done_ = true; ++ ++ if (errno != 0) { ++ return kListError; ++ } ++ ++ return kListDone; ++} ++ ++DirectoryListingEntry::~DirectoryListingEntry() { ++ ResetLink(); ++ if (lister_ != 0) { ++ // This also closes fd_. ++ VOID_NO_RETRY_EXPECTED(closedir(reinterpret_cast(lister_))); ++ } ++} ++ ++void DirectoryListingEntry::ResetLink() { ++ if ((link_ != NULL) && ((parent_ == NULL) || (parent_->link_ != link_))) { ++ delete link_; ++ link_ = NULL; ++ } ++ if (parent_ != NULL) { ++ link_ = parent_->link_; ++ } ++} ++ ++static bool DeleteRecursively(int dirfd, PathBuffer* path); ++ ++static bool DeleteFile(int dirfd, char* file_name, PathBuffer* path) { ++ return path->Add(file_name) && ++ (NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), 0)) == 0); ++} ++ ++static bool DeleteDir(int dirfd, char* dir_name, PathBuffer* path) { ++ if ((strcmp(dir_name, ".") == 0) || (strcmp(dir_name, "..") == 0)) { ++ return true; ++ } ++ return path->Add(dir_name) && DeleteRecursively(dirfd, path); ++} ++ ++static bool DeleteRecursively(int dirfd, PathBuffer* path) { ++ // Do not recurse into links for deletion. Instead delete the link. ++ // If it's a file, delete it. ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY( ++ fstatat64(dirfd, path->AsString(), &st, AT_SYMLINK_NOFOLLOW)) == -1) { ++ return false; ++ } else if (!S_ISDIR(st.st_mode)) { ++ return (NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), 0)) == 0); ++ } ++ ++ if (!path->Add(File::PathSeparator())) { ++ return false; ++ } ++ ++ // Not a link. Attempt to open as a directory and recurse into the ++ // directory. ++ const int fd = ++ TEMP_FAILURE_RETRY(openat64(dirfd, path->AsString(), O_DIRECTORY)); ++ if (fd < 0) { ++ return false; ++ } ++ DIR* dir_pointer; ++ do { ++ dir_pointer = fdopendir(fd); ++ } while ((dir_pointer == NULL) && (errno == EINTR)); ++ if (dir_pointer == NULL) { ++ FDUtils::SaveErrorAndClose(fd); ++ return false; ++ } ++ ++ // Iterate the directory and delete all files and directories. ++ int path_length = path->length(); ++ while (true) { ++ // In case `readdir()` returns `NULL` we distinguish between end-of-stream ++ // and error by looking if `errno` was updated. ++ errno = 0; ++ // In glibc 2.24+, readdir_r is deprecated. ++ // According to the man page for readdir: ++ // "readdir(3) is not required to be thread-safe. However, in modern ++ // implementations (including the glibc implementation), concurrent calls to ++ // readdir(3) that specify different directory streams are thread-safe." ++ dirent* entry = readdir(dir_pointer); ++ if (entry == NULL) { ++ // Failed to read next directory entry. ++ if (errno != 0) { ++ break; ++ } ++ // End of directory. ++ int status = NO_RETRY_EXPECTED(closedir(dir_pointer)); ++ if (status != 0) { ++ return false; ++ } ++ status = ++ NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), AT_REMOVEDIR)); ++ return status == 0; ++ } ++ bool ok = false; ++ switch (entry->d_type) { ++ case DT_DIR: ++ ok = DeleteDir(dirfd, entry->d_name, path); ++ break; ++ case DT_BLK: ++ case DT_CHR: ++ case DT_FIFO: ++ case DT_SOCK: ++ case DT_REG: ++ case DT_LNK: ++ // Treat all links as files. This will delete the link which ++ // is what we want no matter if the link target is a file or a ++ // directory. ++ ok = DeleteFile(dirfd, entry->d_name, path); ++ break; ++ case DT_UNKNOWN: { ++ if (!path->Add(entry->d_name)) { ++ break; ++ } ++ // On some file systems the entry type is not determined by ++ // readdir. For those we use lstat to determine the entry ++ // type. ++ struct stat64 entry_info; ++ if (TEMP_FAILURE_RETRY(fstatat64(dirfd, path->AsString(), &entry_info, ++ AT_SYMLINK_NOFOLLOW)) == -1) { ++ break; ++ } ++ path->Reset(path_length); ++ if (S_ISDIR(entry_info.st_mode)) { ++ ok = DeleteDir(dirfd, entry->d_name, path); ++ } else { ++ // Treat links as files. This will delete the link which is ++ // what we want no matter if the link target is a file or a ++ // directory. ++ ok = DeleteFile(dirfd, entry->d_name, path); ++ } ++ break; ++ } ++ default: ++ // We should have covered all the bases. If not, let's get an error. ++ FATAL("Unexpected d_type: %d\n", entry->d_type); ++ break; ++ } ++ if (!ok) { ++ break; ++ } ++ path->Reset(path_length); ++ } ++ // Only happens if an error. ++ ASSERT(errno != 0); ++ int err = errno; ++ VOID_NO_RETRY_EXPECTED(closedir(dir_pointer)); ++ errno = err; ++ return false; ++} ++ ++Directory::ExistsResult Directory::Exists(Namespace* namespc, ++ const char* dir_name) { ++ NamespaceScope ns(namespc, dir_name); ++ struct stat64 entry_info; ++ int success = ++ TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0)); ++ if (success == 0) { ++ if (S_ISDIR(entry_info.st_mode)) { ++ return EXISTS; ++ } else { ++ // An OSError may be constructed based on the return value of this ++ // function, so set errno to something that makes sense. ++ errno = ENOTDIR; ++ return DOES_NOT_EXIST; ++ } ++ } else { ++ if ((errno == EACCES) || (errno == EBADF) || (errno == EFAULT) || ++ (errno == ENOMEM) || (errno == EOVERFLOW)) { ++ // Search permissions denied for one of the directories in the ++ // path or a low level error occurred. We do not know if the ++ // directory exists. ++ return UNKNOWN; ++ } ++ ASSERT((errno == ELOOP) || (errno == ENAMETOOLONG) || (errno == ENOENT) || ++ (errno == ENOTDIR)); ++ return DOES_NOT_EXIST; ++ } ++} ++ ++char* Directory::CurrentNoScope() { ++ return getcwd(NULL, 0); ++} ++ ++bool Directory::Create(Namespace* namespc, const char* dir_name) { ++ NamespaceScope ns(namespc, dir_name); ++ // Create the directory with the permissions specified by the ++ // process umask. ++ const int result = NO_RETRY_EXPECTED(mkdirat(ns.fd(), ns.path(), 0777)); ++ // If the directory already exists, treat it as a success. ++ if ((result == -1) && (errno == EEXIST)) { ++ return (Exists(namespc, dir_name) == EXISTS); ++ } ++ return (result == 0); ++} ++ ++const char* Directory::SystemTemp(Namespace* namespc) { ++ if (Directory::system_temp_path_override_ != NULL) { ++ return DartUtils::ScopedCopyCString(Directory::system_temp_path_override_); ++ } ++ ++ PathBuffer path; ++ const char* temp_dir = getenv("TMPDIR"); ++ if (temp_dir == NULL) { ++ temp_dir = getenv("TMP"); ++ } ++ if (temp_dir == NULL) { ++ temp_dir = "/tmp"; ++ } ++ NamespaceScope ns(namespc, temp_dir); ++ if (!path.Add(ns.path())) { ++ return NULL; ++ } ++ ++ // Remove any trailing slash. ++ char* result = path.AsString(); ++ int length = strlen(result); ++ if ((length > 1) && (result[length - 1] == '/')) { ++ result[length - 1] = '\0'; ++ } ++ return path.AsScopedString(); ++} ++ ++// Returns a new, unused directory name, adding characters to the end ++// of prefix. Creates the directory with the permissions specified ++// by the process umask. ++// The return value is Dart_ScopeAllocated. ++const char* Directory::CreateTemp(Namespace* namespc, const char* prefix) { ++ PathBuffer path; ++ const int firstchar = 'A'; ++ const int numchars = 'Z' - 'A' + 1; ++ uint8_t random_bytes[7]; ++ ++ // mkdtemp doesn't have an "at" variant, so we have to simulate it. ++ if (!path.Add(prefix)) { ++ return NULL; ++ } ++ intptr_t prefix_length = path.length(); ++ while (true) { ++ Crypto::GetRandomBytes(6, random_bytes); ++ for (intptr_t i = 0; i < 6; i++) { ++ random_bytes[i] = (random_bytes[i] % numchars) + firstchar; ++ } ++ random_bytes[6] = '\0'; ++ if (!path.Add(reinterpret_cast(random_bytes))) { ++ return NULL; ++ } ++ NamespaceScope ns(namespc, path.AsString()); ++ const int result = NO_RETRY_EXPECTED(mkdirat(ns.fd(), ns.path(), 0777)); ++ if (result == 0) { ++ return path.AsScopedString(); ++ } else if (errno == EEXIST) { ++ path.Reset(prefix_length); ++ } else { ++ return NULL; ++ } ++ } ++} ++ ++bool Directory::Delete(Namespace* namespc, ++ const char* dir_name, ++ bool recursive) { ++ NamespaceScope ns(namespc, dir_name); ++ if (!recursive) { ++ if ((File::GetType(namespc, dir_name, false) == File::kIsLink) && ++ (File::GetType(namespc, dir_name, true) == File::kIsDirectory)) { ++ return NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0; ++ } ++ return NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), AT_REMOVEDIR)) == 0; ++ } else { ++ PathBuffer path; ++ if (!path.Add(ns.path())) { ++ return false; ++ } ++ return DeleteRecursively(ns.fd(), &path); ++ } ++} ++ ++bool Directory::Rename(Namespace* namespc, ++ const char* old_path, ++ const char* new_path) { ++ ExistsResult exists = Exists(namespc, old_path); ++ if (exists != EXISTS) { ++ return false; ++ } ++ NamespaceScope oldns(namespc, old_path); ++ NamespaceScope newns(namespc, new_path); ++ return (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(), ++ newns.path())) == 0); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/eventhandler.h b/runtime/bin/eventhandler.h +index d35fc539ab1..b49bdbc705c 100644 +--- a/runtime/bin/eventhandler.h ++++ b/runtime/bin/eventhandler.h +@@ -593,6 +593,8 @@ class DescriptorInfoMultipleMixin : public DI { + #include "bin/eventhandler_macos.h" + #elif defined(DART_HOST_OS_WINDOWS) + #include "bin/eventhandler_win.h" ++#elif defined(DART_HOST_OS_OHOS) ++#include "bin/eventhandler_ohos.h" + #else + #error Unknown target os. + #endif +diff --git a/runtime/bin/eventhandler_ohos.cc b/runtime/bin/eventhandler_ohos.cc +new file mode 100644 +index 00000000000..38d769d435b +--- /dev/null ++++ b/runtime/bin/eventhandler_ohos.cc +@@ -0,0 +1,443 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/eventhandler.h" ++#include "bin/eventhandler_ohos.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/dartutils.h" ++#include "bin/fdutils.h" ++#include "bin/lockers.h" ++#include "bin/process.h" ++#include "bin/socket.h" ++#include "bin/thread.h" ++#include "platform/syslog.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++intptr_t DescriptorInfo::GetPollEvents() { ++ // Do not ask for EPOLLERR and EPOLLHUP explicitly as they are ++ // triggered anyway. ++ intptr_t events = 0; ++ if ((Mask() & (1 << kInEvent)) != 0) { ++ events |= EPOLLIN; ++ } ++ if ((Mask() & (1 << kOutEvent)) != 0) { ++ events |= EPOLLOUT; ++ } ++ return events; ++} ++ ++// Unregister the file descriptor for a DescriptorInfo structure with ++// epoll. ++static void RemoveFromEpollInstance(intptr_t epoll_fd_, DescriptorInfo* di) { ++ VOID_NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, di->fd(), NULL)); ++} ++ ++static void AddToEpollInstance(intptr_t epoll_fd_, DescriptorInfo* di) { ++ struct epoll_event event; ++ event.events = EPOLLRDHUP | di->GetPollEvents(); ++ if (!di->IsListeningSocket()) { ++ event.events |= EPOLLET; ++ } ++ event.data.ptr = di; ++ int status = ++ NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, di->fd(), &event)); ++ if (status == -1) { ++ // TODO(dart:io): Verify that the dart end is handling this correctly. ++ ++ // Epoll does not accept the file descriptor. It could be due to ++ // already closed file descriptor, or unuspported devices, such ++ // as /dev/null. In such case, mark the file descriptor as closed, ++ // so dart will handle it accordingly. ++ di->NotifyAllDartPorts(1 << kCloseEvent); ++ } ++} ++ ++EventHandlerImplementation::EventHandlerImplementation() ++ : socket_map_(&SimpleHashMap::SamePointerValue, 16) { ++ intptr_t result; ++ result = NO_RETRY_EXPECTED(pipe(interrupt_fds_)); ++ if (result != 0) { ++ FATAL("Pipe creation failed"); ++ } ++ if (!FDUtils::SetNonBlocking(interrupt_fds_[0])) { ++ FATAL("Failed to set pipe fd non blocking\n"); ++ } ++ if (!FDUtils::SetCloseOnExec(interrupt_fds_[0])) { ++ FATAL("Failed to set pipe fd close on exec\n"); ++ } ++ if (!FDUtils::SetCloseOnExec(interrupt_fds_[1])) { ++ FATAL("Failed to set pipe fd close on exec\n"); ++ } ++ shutdown_ = false; ++ // The initial size passed to epoll_create is ignore on newer (>= ++ // 2.6.8) Linux versions ++ static const int kEpollInitialSize = 64; ++ epoll_fd_ = NO_RETRY_EXPECTED(epoll_create(kEpollInitialSize)); ++ if (epoll_fd_ == -1) { ++ FATAL("Failed creating epoll file descriptor: %i", errno); ++ } ++ if (!FDUtils::SetCloseOnExec(epoll_fd_)) { ++ FATAL("Failed to set epoll fd close on exec\n"); ++ } ++ // Register the interrupt_fd with the epoll instance. ++ struct epoll_event event; ++ event.events = EPOLLIN; ++ event.data.ptr = NULL; ++ int status = NO_RETRY_EXPECTED( ++ epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupt_fds_[0], &event)); ++ if (status == -1) { ++ FATAL("Failed adding interrupt fd to epoll instance"); ++ } ++ timer_fd_ = NO_RETRY_EXPECTED(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)); ++ if (timer_fd_ == -1) { ++ FATAL("Failed creating timerfd file descriptor: %i", errno); ++ } ++ // Register the timer_fd_ with the epoll instance. ++ event.events = EPOLLIN; ++ event.data.fd = timer_fd_; ++ status = ++ NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &event)); ++ if (status == -1) { ++ FATAL("Failed adding timerfd fd(%i) to epoll instance: %i", timer_fd_, ++ errno); ++ } ++} ++ ++static void DeleteDescriptorInfo(void* info) { ++ DescriptorInfo* di = reinterpret_cast(info); ++ di->Close(); ++ delete di; ++} ++ ++EventHandlerImplementation::~EventHandlerImplementation() { ++ socket_map_.Clear(DeleteDescriptorInfo); ++ close(epoll_fd_); ++ close(timer_fd_); ++ close(interrupt_fds_[0]); ++ close(interrupt_fds_[1]); ++} ++ ++void EventHandlerImplementation::UpdateEpollInstance(intptr_t old_mask, ++ DescriptorInfo* di) { ++ intptr_t new_mask = di->Mask(); ++ if ((old_mask != 0) && (new_mask == 0)) { ++ RemoveFromEpollInstance(epoll_fd_, di); ++ } else if ((old_mask == 0) && (new_mask != 0)) { ++ AddToEpollInstance(epoll_fd_, di); ++ } else if ((old_mask != 0) && (new_mask != 0) && (old_mask != new_mask)) { ++ ASSERT(!di->IsListeningSocket()); ++ RemoveFromEpollInstance(epoll_fd_, di); ++ AddToEpollInstance(epoll_fd_, di); ++ } ++} ++ ++DescriptorInfo* EventHandlerImplementation::GetDescriptorInfo( ++ intptr_t fd, ++ bool is_listening) { ++ ASSERT(fd >= 0); ++ SimpleHashMap::Entry* entry = socket_map_.Lookup( ++ GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd), true); ++ ASSERT(entry != NULL); ++ DescriptorInfo* di = reinterpret_cast(entry->value); ++ if (di == NULL) { ++ // If there is no data in the hash map for this file descriptor a ++ // new DescriptorInfo for the file descriptor is inserted. ++ if (is_listening) { ++ di = new DescriptorInfoMultiple(fd); ++ } else { ++ di = new DescriptorInfoSingle(fd); ++ } ++ entry->value = di; ++ } ++ ASSERT(fd == di->fd()); ++ return di; ++} ++ ++void EventHandlerImplementation::WakeupHandler(intptr_t id, ++ Dart_Port dart_port, ++ int64_t data) { ++ InterruptMessage msg; ++ msg.id = id; ++ msg.dart_port = dart_port; ++ msg.data = data; ++ // WriteToBlocking will write up to 512 bytes atomically, and since our msg ++ // is smaller than 512, we don't need a thread lock. ++ // See: http://linux.die.net/man/7/pipe, section 'Pipe_buf'. ++ ASSERT(kInterruptMessageSize < PIPE_BUF); ++ intptr_t result = ++ FDUtils::WriteToBlocking(interrupt_fds_[1], &msg, kInterruptMessageSize); ++ if (result != kInterruptMessageSize) { ++ if (result == -1) { ++ perror("Interrupt message failure:"); ++ } ++ FATAL("Interrupt message failure. Wrote %" Pd " bytes.", result); ++ } ++} ++ ++void EventHandlerImplementation::HandleInterruptFd() { ++ const intptr_t MAX_MESSAGES = kInterruptMessageSize; ++ InterruptMessage msg[MAX_MESSAGES]; ++ ssize_t bytes = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ read(interrupt_fds_[0], msg, MAX_MESSAGES * kInterruptMessageSize)); ++ for (ssize_t i = 0; i < bytes / kInterruptMessageSize; i++) { ++ if (msg[i].id == kTimerId) { ++ timeout_queue_.UpdateTimeout(msg[i].dart_port, msg[i].data); ++ UpdateTimerFd(); ++ } else if (msg[i].id == kShutdownId) { ++ shutdown_ = true; ++ } else { ++ ASSERT((msg[i].data & COMMAND_MASK) != 0); ++ Socket* socket = reinterpret_cast(msg[i].id); ++ RefCntReleaseScope rs(socket); ++ if (socket->fd() == -1) { ++ continue; ++ } ++ DescriptorInfo* di = ++ GetDescriptorInfo(socket->fd(), IS_LISTENING_SOCKET(msg[i].data)); ++ if (IS_COMMAND(msg[i].data, kShutdownReadCommand)) { ++ ASSERT(!di->IsListeningSocket()); ++ // Close the socket for reading. ++ VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_RD)); ++ } else if (IS_COMMAND(msg[i].data, kShutdownWriteCommand)) { ++ ASSERT(!di->IsListeningSocket()); ++ // Close the socket for writing. ++ VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_WR)); ++ } else if (IS_COMMAND(msg[i].data, kCloseCommand)) { ++ // Close the socket and free system resources and move on to next ++ // message. ++ if (IS_SIGNAL_SOCKET(msg[i].data)) { ++ Process::ClearSignalHandlerByFd(di->fd(), socket->isolate_port()); ++ } ++ intptr_t old_mask = di->Mask(); ++ Dart_Port port = msg[i].dart_port; ++ if (port != ILLEGAL_PORT) { ++ di->RemovePort(port); ++ } ++ intptr_t new_mask = di->Mask(); ++ UpdateEpollInstance(old_mask, di); ++ ++ intptr_t fd = di->fd(); ++ ASSERT(fd == socket->fd()); ++ if (di->IsListeningSocket()) { ++ // We only close the socket file descriptor from the operating ++ // system if there are no other dart socket objects which ++ // are listening on the same (address, port) combination. ++ ListeningSocketRegistry* registry = ++ ListeningSocketRegistry::Instance(); ++ ++ MutexLocker locker(registry->mutex()); ++ ++ if (registry->CloseSafe(socket)) { ++ ASSERT(new_mask == 0); ++ socket_map_.Remove(GetHashmapKeyFromFd(fd), ++ GetHashmapHashFromFd(fd)); ++ di->Close(); ++ delete di; ++ } ++ socket->CloseFd(); ++ } else { ++ ASSERT(new_mask == 0); ++ socket_map_.Remove(GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd)); ++ di->Close(); ++ delete di; ++ socket->CloseFd(); ++ } ++ DartUtils::PostInt32(port, 1 << kDestroyedEvent); ++ } else if (IS_COMMAND(msg[i].data, kReturnTokenCommand)) { ++ int count = TOKEN_COUNT(msg[i].data); ++ intptr_t old_mask = di->Mask(); ++ di->ReturnTokens(msg[i].dart_port, count); ++ UpdateEpollInstance(old_mask, di); ++ } else if (IS_COMMAND(msg[i].data, kSetEventMaskCommand)) { ++ // `events` can only have kInEvent/kOutEvent flags set. ++ intptr_t events = msg[i].data & EVENT_MASK; ++ ASSERT(0 == (events & ~(1 << kInEvent | 1 << kOutEvent))); ++ ++ intptr_t old_mask = di->Mask(); ++ di->SetPortAndMask(msg[i].dart_port, msg[i].data & EVENT_MASK); ++ UpdateEpollInstance(old_mask, di); ++ } else { ++ UNREACHABLE(); ++ } ++ } ++ } ++} ++ ++void EventHandlerImplementation::UpdateTimerFd() { ++ struct itimerspec it; ++ memset(&it, 0, sizeof(it)); ++ if (timeout_queue_.HasTimeout()) { ++ int64_t millis = timeout_queue_.CurrentTimeout(); ++ it.it_value.tv_sec = millis / 1000; ++ it.it_value.tv_nsec = (millis % 1000) * 1000000; ++ } ++ VOID_NO_RETRY_EXPECTED( ++ timerfd_settime(timer_fd_, TFD_TIMER_ABSTIME, &it, NULL)); ++} ++ ++#ifdef DEBUG_POLL ++static void PrintEventMask(intptr_t fd, intptr_t events) { ++ Syslog::Print("%d ", fd); ++ if ((events & EPOLLIN) != 0) { ++ Syslog::Print("EPOLLIN "); ++ } ++ if ((events & EPOLLPRI) != 0) { ++ Syslog::Print("EPOLLPRI "); ++ } ++ if ((events & EPOLLOUT) != 0) { ++ Syslog::Print("EPOLLOUT "); ++ } ++ if ((events & EPOLLERR) != 0) { ++ Syslog::Print("EPOLLERR "); ++ } ++ if ((events & EPOLLHUP) != 0) { ++ Syslog::Print("EPOLLHUP "); ++ } ++ if ((events & EPOLLRDHUP) != 0) { ++ Syslog::Print("EPOLLRDHUP "); ++ } ++ int all_events = ++ EPOLLIN | EPOLLPRI | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDHUP; ++ if ((events & ~all_events) != 0) { ++ Syslog::Print("(and %08x) ", events & ~all_events); ++ } ++ Syslog::Print("(available %d) ", FDUtils::AvailableBytes(fd)); ++ ++ Syslog::Print("\n"); ++} ++#endif ++ ++intptr_t EventHandlerImplementation::GetPollEvents(intptr_t events, ++ DescriptorInfo* di) { ++#ifdef DEBUG_POLL ++ PrintEventMask(di->fd(), events); ++#endif ++ if ((events & EPOLLERR) != 0) { ++ // Return error only if EPOLLIN is present. ++ return ((events & EPOLLIN) != 0) ? (1 << kErrorEvent) : 0; ++ } ++ intptr_t event_mask = 0; ++ if ((events & EPOLLIN) != 0) { ++ event_mask |= (1 << kInEvent); ++ } ++ if ((events & EPOLLOUT) != 0) { ++ event_mask |= (1 << kOutEvent); ++ } ++ if ((events & (EPOLLHUP | EPOLLRDHUP)) != 0) { ++ event_mask |= (1 << kCloseEvent); ++ } ++ return event_mask; ++} ++ ++void EventHandlerImplementation::HandleEvents(struct epoll_event* events, ++ int size) { ++ bool interrupt_seen = false; ++ for (int i = 0; i < size; i++) { ++ if (events[i].data.ptr == NULL) { ++ interrupt_seen = true; ++ } else if (events[i].data.fd == timer_fd_) { ++ int64_t val; ++ VOID_TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ read(timer_fd_, &val, sizeof(val))); ++ if (timeout_queue_.HasTimeout()) { ++ DartUtils::PostNull(timeout_queue_.CurrentPort()); ++ timeout_queue_.RemoveCurrent(); ++ } ++ UpdateTimerFd(); ++ } else { ++ DescriptorInfo* di = ++ reinterpret_cast(events[i].data.ptr); ++ const intptr_t old_mask = di->Mask(); ++ const intptr_t event_mask = GetPollEvents(events[i].events, di); ++ if ((event_mask & (1 << kErrorEvent)) != 0) { ++ di->NotifyAllDartPorts(event_mask); ++ UpdateEpollInstance(old_mask, di); ++ } else if (event_mask != 0) { ++ Dart_Port port = di->NextNotifyDartPort(event_mask); ++ ASSERT(port != 0); ++ UpdateEpollInstance(old_mask, di); ++ DartUtils::PostInt32(port, event_mask); ++ } ++ } ++ } ++ if (interrupt_seen) { ++ // Handle after socket events, so we avoid closing a socket before we handle ++ // the current events. ++ HandleInterruptFd(); ++ } ++} ++ ++void EventHandlerImplementation::Poll(uword args) { ++ ThreadSignalBlocker signal_blocker(SIGPROF); ++ static const intptr_t kMaxEvents = 16; ++ struct epoll_event events[kMaxEvents]; ++ EventHandler* handler = reinterpret_cast(args); ++ EventHandlerImplementation* handler_impl = &handler->delegate_; ++ ASSERT(handler_impl != NULL); ++ ++ while (!handler_impl->shutdown_) { ++ intptr_t result = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ epoll_wait(handler_impl->epoll_fd_, events, kMaxEvents, -1)); ++ ASSERT(EAGAIN == EWOULDBLOCK); ++ if (result <= 0) { ++ if (errno != EWOULDBLOCK) { ++ perror("Poll failed"); ++ } ++ } else { ++ handler_impl->HandleEvents(events, result); ++ } ++ } ++ DEBUG_ASSERT(ReferenceCounted::instances() == 0); ++ handler->NotifyShutdownDone(); ++} ++ ++void EventHandlerImplementation::Start(EventHandler* handler) { ++ int result = ++ Thread::Start("dart:io EventHandler", &EventHandlerImplementation::Poll, ++ reinterpret_cast(handler)); ++ if (result != 0) { ++ FATAL("Failed to start event handler thread %d", result); ++ } ++} ++ ++void EventHandlerImplementation::Shutdown() { ++ SendData(kShutdownId, 0, 0); ++} ++ ++void EventHandlerImplementation::SendData(intptr_t id, ++ Dart_Port dart_port, ++ int64_t data) { ++ WakeupHandler(id, dart_port, data); ++} ++ ++void* EventHandlerImplementation::GetHashmapKeyFromFd(intptr_t fd) { ++ // The hashmap does not support keys with value 0. ++ return reinterpret_cast(fd + 1); ++} ++ ++uint32_t EventHandlerImplementation::GetHashmapHashFromFd(intptr_t fd) { ++ // The hashmap does not support keys with value 0. ++ return dart::Utils::WordHash(fd + 1); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/eventhandler_ohos.h b/runtime/bin/eventhandler_ohos.h +new file mode 100644 +index 00000000000..0e4d84c8c12 +--- /dev/null ++++ b/runtime/bin/eventhandler_ohos.h +@@ -0,0 +1,99 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#ifndef RUNTIME_BIN_EVENTHANDLER_OHOS_H_ ++#define RUNTIME_BIN_EVENTHANDLER_OHOS_H_ ++ ++#if !defined(RUNTIME_BIN_EVENTHANDLER_H_) ++#error Do not include eventhandler_linux.h directly; use eventhandler.h instead. ++#endif ++ ++#include ++#include ++#include ++#include ++ ++#include "platform/hashmap.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++class DescriptorInfo : public DescriptorInfoBase { ++ public: ++ explicit DescriptorInfo(intptr_t fd) : DescriptorInfoBase(fd) {} ++ ++ virtual ~DescriptorInfo() {} ++ ++ intptr_t GetPollEvents(); ++ ++ virtual void Close() { ++ close(fd_); ++ fd_ = -1; ++ } ++ ++ private: ++ DISALLOW_COPY_AND_ASSIGN(DescriptorInfo); ++}; ++ ++class DescriptorInfoSingle : public DescriptorInfoSingleMixin { ++ public: ++ explicit DescriptorInfoSingle(intptr_t fd) ++ : DescriptorInfoSingleMixin(fd, false) {} ++ virtual ~DescriptorInfoSingle() {} ++ ++ private: ++ DISALLOW_COPY_AND_ASSIGN(DescriptorInfoSingle); ++}; ++ ++class DescriptorInfoMultiple ++ : public DescriptorInfoMultipleMixin { ++ public: ++ explicit DescriptorInfoMultiple(intptr_t fd) ++ : DescriptorInfoMultipleMixin(fd, false) {} ++ virtual ~DescriptorInfoMultiple() {} ++ ++ private: ++ DISALLOW_COPY_AND_ASSIGN(DescriptorInfoMultiple); ++}; ++ ++class EventHandlerImplementation { ++ public: ++ EventHandlerImplementation(); ++ ~EventHandlerImplementation(); ++ ++ void UpdateEpollInstance(intptr_t old_mask, DescriptorInfo* di); ++ ++ // Gets the socket data structure for a given file ++ // descriptor. Creates a new one if one is not found. ++ DescriptorInfo* GetDescriptorInfo(intptr_t fd, bool is_listening); ++ void SendData(intptr_t id, Dart_Port dart_port, int64_t data); ++ void Start(EventHandler* handler); ++ void Shutdown(); ++ ++ private: ++ void HandleEvents(struct epoll_event* events, int size); ++ static void Poll(uword args); ++ void WakeupHandler(intptr_t id, Dart_Port dart_port, int64_t data); ++ void HandleInterruptFd(); ++ void UpdateTimerFd(); ++ void SetPort(intptr_t fd, Dart_Port dart_port, intptr_t mask); ++ intptr_t GetPollEvents(intptr_t events, DescriptorInfo* di); ++ static void* GetHashmapKeyFromFd(intptr_t fd); ++ static uint32_t GetHashmapHashFromFd(intptr_t fd); ++ ++ SimpleHashMap socket_map_; ++ TimeoutQueue timeout_queue_; ++ bool shutdown_; ++ int interrupt_fds_[2]; ++ int epoll_fd_; ++ int timer_fd_; ++ ++ DISALLOW_COPY_AND_ASSIGN(EventHandlerImplementation); ++}; ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // RUNTIME_BIN_EVENTHANDLER_OHOS_H_ +diff --git a/runtime/bin/fdutils_ohos.cc b/runtime/bin/fdutils_ohos.cc +new file mode 100644 +index 00000000000..f7531006cbe +--- /dev/null ++++ b/runtime/bin/fdutils_ohos.cc +@@ -0,0 +1,142 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/fdutils.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool FDUtils::SetCloseOnExec(intptr_t fd) { ++ intptr_t status; ++ status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFD)); ++ if (status < 0) { ++ perror("fcntl(F_GETFD) failed"); ++ return false; ++ } ++ status |= FD_CLOEXEC; ++ if (NO_RETRY_EXPECTED(fcntl(fd, F_SETFD, status)) < 0) { ++ perror("fcntl(F_SETFD, FD_CLOEXEC) failed"); ++ return false; ++ } ++ return true; ++} ++ ++static bool SetBlockingHelper(intptr_t fd, bool blocking) { ++ intptr_t status; ++ status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFL)); ++ if (status < 0) { ++ perror("fcntl(F_GETFL) failed"); ++ return false; ++ } ++ status = blocking ? (status & ~O_NONBLOCK) : (status | O_NONBLOCK); ++ if (NO_RETRY_EXPECTED(fcntl(fd, F_SETFL, status)) < 0) { ++ perror("fcntl(F_SETFL, O_NONBLOCK) failed"); ++ return false; ++ } ++ return true; ++} ++ ++bool FDUtils::SetNonBlocking(intptr_t fd) { ++ return SetBlockingHelper(fd, false); ++} ++ ++bool FDUtils::SetBlocking(intptr_t fd) { ++ return SetBlockingHelper(fd, true); ++} ++ ++bool FDUtils::IsBlocking(intptr_t fd, bool* is_blocking) { ++ intptr_t status; ++ status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFL)); ++ if (status < 0) { ++ return false; ++ } ++ *is_blocking = (status & O_NONBLOCK) == 0; ++ return true; ++} ++ ++intptr_t FDUtils::AvailableBytes(intptr_t fd) { ++ int available; // ioctl for FIONREAD expects an 'int*' argument. ++ int result = NO_RETRY_EXPECTED(ioctl(fd, FIONREAD, &available)); ++ if (result < 0) { ++ return result; ++ } ++ ASSERT(available >= 0); ++ return static_cast(available); ++} ++ ++ssize_t FDUtils::ReadFromBlocking(int fd, void* buffer, size_t count) { ++#ifdef DEBUG ++ bool is_blocking = false; ++ ASSERT(FDUtils::IsBlocking(fd, &is_blocking)); ++ ASSERT(is_blocking); ++#endif ++ size_t remaining = count; ++ char* buffer_pos = reinterpret_cast(buffer); ++ while (remaining > 0) { ++ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer_pos, remaining)); ++ if (bytes_read == 0) { ++ return count - remaining; ++ } else if (bytes_read == -1) { ++ ASSERT(EAGAIN == EWOULDBLOCK); ++ // Error code EWOULDBLOCK should only happen for non blocking ++ // file descriptors. ++ ASSERT(errno != EWOULDBLOCK); ++ return -1; ++ } else { ++ ASSERT(bytes_read > 0); ++ remaining -= bytes_read; ++ buffer_pos += bytes_read; ++ } ++ } ++ return count; ++} ++ ++ssize_t FDUtils::WriteToBlocking(int fd, const void* buffer, size_t count) { ++#ifdef DEBUG ++ bool is_blocking = false; ++ ASSERT(FDUtils::IsBlocking(fd, &is_blocking)); ++ ASSERT(is_blocking); ++#endif ++ size_t remaining = count; ++ char* buffer_pos = const_cast(reinterpret_cast(buffer)); ++ while (remaining > 0) { ++ ssize_t bytes_written = ++ TEMP_FAILURE_RETRY(write(fd, buffer_pos, remaining)); ++ if (bytes_written == 0) { ++ return count - remaining; ++ } else if (bytes_written == -1) { ++ ASSERT(EAGAIN == EWOULDBLOCK); ++ // Error code EWOULDBLOCK should only happen for non blocking ++ // file descriptors. ++ ASSERT(errno != EWOULDBLOCK); ++ return -1; ++ } else { ++ ASSERT(bytes_written > 0); ++ remaining -= bytes_written; ++ buffer_pos += bytes_written; ++ } ++ } ++ return count; ++} ++ ++void FDUtils::SaveErrorAndClose(intptr_t fd) { ++ int err = errno; ++ close(fd); ++ errno = err; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc +index a66574162f2..ff34aa04738 100644 +--- a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc ++++ b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc +@@ -1295,7 +1295,7 @@ DART_EXPORT void WaitUntilNThreadsEnterBarrier(intptr_t num_threads) { + //////////////////////////////////////////////////////////////////////////////// + + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || \ +- defined(DART_HOST_OS_MACOS) ++ defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + static bool Regress216834909_hang_at_exit = true; + + static void Regress216834909_AtExit() { +@@ -1317,7 +1317,7 @@ DART_EXPORT void Regress216834909_SetAtExit(int64_t install) { + } + } + #endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || +- // defined(DART_HOST_OS_MACOS) ++ // defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + + DART_EXPORT bool IsNull(Dart_Handle object) { + return Dart_IsNull(object); +diff --git a/runtime/bin/ffi_unit_test/BUILD.gn b/runtime/bin/ffi_unit_test/BUILD.gn +index 430b908d86b..b3c1f15b9b9 100644 +--- a/runtime/bin/ffi_unit_test/BUILD.gn ++++ b/runtime/bin/ffi_unit_test/BUILD.gn +@@ -73,6 +73,10 @@ config("define_target_os_linux") { + defines = [ "DART_TARGET_OS_LINUX" ] + } + ++config("define_target_os_ohos") { ++ defines = [ "DART_TARGET_OS_OHOS" ] ++} ++ + config("define_target_os_macos") { + defines = [ "DART_TARGET_OS_MACOS" ] + } +diff --git a/runtime/bin/file_ohos.cc b/runtime/bin/file_ohos.cc +new file mode 100644 +index 00000000000..8ac60ad5e64 +--- /dev/null ++++ b/runtime/bin/file_ohos.cc +@@ -0,0 +1,787 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/file.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/builtin.h" ++#include "bin/fdutils.h" ++#include "bin/namespace.h" ++#include "platform/signal_blocker.h" ++#include "platform/syslog.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++class FileHandle { ++ public: ++ explicit FileHandle(int fd) : fd_(fd) {} ++ ~FileHandle() {} ++ int fd() const { return fd_; } ++ void set_fd(int fd) { fd_ = fd; } ++ ++ private: ++ int fd_; ++ ++ DISALLOW_COPY_AND_ASSIGN(FileHandle); ++}; ++ ++File::~File() { ++ if (!IsClosed() && (handle_->fd() != STDOUT_FILENO) && ++ (handle_->fd() != STDERR_FILENO)) { ++ Close(); ++ } ++ delete handle_; ++} ++ ++void File::Close() { ++ ASSERT(handle_->fd() >= 0); ++ if (handle_->fd() == STDOUT_FILENO) { ++ // If stdout, redirect fd to /dev/null. ++ int null_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY)); ++ ASSERT(null_fd >= 0); ++ VOID_TEMP_FAILURE_RETRY(dup2(null_fd, handle_->fd())); ++ close(null_fd); ++ } else { ++ int err = close(handle_->fd()); ++ if (err != 0) { ++ const int kBufferSize = 1024; ++ char error_buf[kBufferSize]; ++ Syslog::PrintErr("%s\n", Utils::StrError(errno, error_buf, kBufferSize)); ++ } ++ } ++ handle_->set_fd(kClosedFd); ++} ++ ++intptr_t File::GetFD() { ++ return handle_->fd(); ++} ++ ++bool File::IsClosed() { ++ return handle_->fd() == kClosedFd; ++} ++ ++MappedMemory* File::Map(MapType type, ++ int64_t position, ++ int64_t length, ++ void* start) { ++ ASSERT(handle_->fd() >= 0); ++ ASSERT(length > 0); ++ void* hint = nullptr; ++ int prot = PROT_NONE; ++ int flags = MAP_PRIVATE; ++ switch (type) { ++ case kReadOnly: ++ prot = PROT_READ; ++ break; ++ case kReadExecute: ++ // Try to allocate near the VM's binary. ++ hint = reinterpret_cast(&Dart_Initialize); ++ prot = PROT_READ | PROT_EXEC; ++ flags |= (MAP_JIT_OHOS | MAP_ANONYMOUS); ++ break; ++ case kReadWrite: ++ prot = PROT_READ | PROT_WRITE; ++ break; ++ } ++ if (start != nullptr) { ++ hint = start; ++ flags |= MAP_FIXED; ++ } ++ void* addr = mmap(hint, length, prot, flags, handle_->fd(), position); ++ if (addr == MAP_FAILED) { ++ return NULL; ++ } ++ return new MappedMemory(addr, length, /*should_unmap=*/start == nullptr); ++} ++ ++void MappedMemory::Unmap() { ++ int result = munmap(address_, size_); ++ ASSERT(result == 0); ++ address_ = 0; ++ size_ = 0; ++} ++ ++int64_t File::Read(void* buffer, int64_t num_bytes) { ++ ASSERT(handle_->fd() >= 0); ++ return TEMP_FAILURE_RETRY(read(handle_->fd(), buffer, num_bytes)); ++} ++ ++int64_t File::Write(const void* buffer, int64_t num_bytes) { ++ ASSERT(handle_->fd() >= 0); ++ return TEMP_FAILURE_RETRY(write(handle_->fd(), buffer, num_bytes)); ++} ++ ++bool File::VPrint(const char* format, va_list args) { ++ // Measure. ++ va_list measure_args; ++ va_copy(measure_args, args); ++ intptr_t len = vsnprintf(NULL, 0, format, measure_args); ++ va_end(measure_args); ++ ++ char* buffer = reinterpret_cast(malloc(len + 1)); ++ ++ // Print. ++ va_list print_args; ++ va_copy(print_args, args); ++ vsnprintf(buffer, len + 1, format, print_args); ++ va_end(print_args); ++ ++ bool result = WriteFully(buffer, len); ++ free(buffer); ++ return result; ++} ++ ++int64_t File::Position() { ++ ASSERT(handle_->fd() >= 0); ++ return NO_RETRY_EXPECTED(lseek64(handle_->fd(), 0, SEEK_CUR)); ++} ++ ++bool File::SetPosition(int64_t position) { ++ ASSERT(handle_->fd() >= 0); ++ return NO_RETRY_EXPECTED(lseek64(handle_->fd(), position, SEEK_SET)) >= 0; ++} ++ ++bool File::Truncate(int64_t length) { ++ ASSERT(handle_->fd() >= 0); ++ return TEMP_FAILURE_RETRY(ftruncate64(handle_->fd(), length) != -1); ++} ++ ++bool File::Flush() { ++ ASSERT(handle_->fd() >= 0); ++ return NO_RETRY_EXPECTED(fsync(handle_->fd())) != -1; ++} ++ ++bool File::Lock(File::LockType lock, int64_t start, int64_t end) { ++ ASSERT(handle_->fd() >= 0); ++ ASSERT((end == -1) || (end > start)); ++ struct flock fl; ++ switch (lock) { ++ case File::kLockUnlock: ++ fl.l_type = F_UNLCK; ++ break; ++ case File::kLockShared: ++ case File::kLockBlockingShared: ++ fl.l_type = F_RDLCK; ++ break; ++ case File::kLockExclusive: ++ case File::kLockBlockingExclusive: ++ fl.l_type = F_WRLCK; ++ break; ++ default: ++ return false; ++ } ++ fl.l_whence = SEEK_SET; ++ fl.l_start = start; ++ fl.l_len = end == -1 ? 0 : end - start; ++ int cmd = F_SETLK; ++ if ((lock == File::kLockBlockingShared) || ++ (lock == File::kLockBlockingExclusive)) { ++ cmd = F_SETLKW; ++ } ++ return TEMP_FAILURE_RETRY(fcntl(handle_->fd(), cmd, &fl)) != -1; ++} ++ ++int64_t File::Length() { ++ ASSERT(handle_->fd() >= 0); ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstat64(handle_->fd(), &st)) == 0) { ++ return st.st_size; ++ } ++ return -1; ++} ++ ++File* File::FileOpenW(const wchar_t* system_name, FileOpenMode mode) { ++ UNREACHABLE(); ++ return NULL; ++} ++ ++File* File::OpenFD(int fd) { ++ return new File(new FileHandle(fd)); ++} ++ ++File* File::Open(Namespace* namespc, const char* name, FileOpenMode mode) { ++ NamespaceScope ns(namespc, name); ++ // Report errors for non-regular files. ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) { ++ // Only accept regular files, character devices, and pipes. ++ if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode) && !S_ISFIFO(st.st_mode)) { ++ errno = (S_ISDIR(st.st_mode)) ? EISDIR : ENOENT; ++ return NULL; ++ } ++ } ++ int flags = O_RDONLY; ++ if ((mode & kWrite) != 0) { ++ ASSERT((mode & kWriteOnly) == 0); ++ flags = (O_RDWR | O_CREAT); ++ } ++ if ((mode & kWriteOnly) != 0) { ++ ASSERT((mode & kWrite) == 0); ++ flags = (O_WRONLY | O_CREAT); ++ } ++ if ((mode & kTruncate) != 0) { ++ flags = flags | O_TRUNC; ++ } ++ flags |= O_CLOEXEC; ++ const int fd = TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), flags, 0666)); ++ if (fd < 0) { ++ return NULL; ++ } ++ if ((((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) || ++ (((mode & kWriteOnly) != 0) && ((mode & kTruncate) == 0))) { ++ int64_t position = NO_RETRY_EXPECTED(lseek64(fd, 0, SEEK_END)); ++ if (position < 0) { ++ return NULL; ++ } ++ } ++ return OpenFD(fd); ++} ++ ++Utils::CStringUniquePtr File::UriToPath(const char* uri) { ++ const char* path = (strlen(uri) >= 8 && strncmp(uri, "file:///", 8) == 0) ++ ? uri + 7 : uri; ++ UriDecoder uri_decoder(path); ++ if (uri_decoder.decoded() == nullptr) { ++ errno = EINVAL; ++ return Utils::CreateCStringUniquePtr(nullptr); ++ } ++ return Utils::CreateCStringUniquePtr(strdup(uri_decoder.decoded())); ++} ++ ++File* File::OpenUri(Namespace* namespc, const char* uri, FileOpenMode mode) { ++ auto path = UriToPath(uri); ++ if (path == nullptr) { ++ return nullptr; ++ } ++ return File::Open(namespc, path.get(), mode); ++} ++ ++File* File::OpenStdio(int fd) { ++ return new File(new FileHandle(fd)); ++} ++ ++bool File::Exists(Namespace* namespc, const char* name) { ++ NamespaceScope ns(namespc, name); ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) { ++ // Everything but a directory and a link is a file to Dart. ++ return !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode); ++ } else { ++ return false; ++ } ++} ++ ++bool File::ExistsUri(Namespace* namespc, const char* uri) { ++ auto path = UriToPath(uri); ++ if (path == nullptr) { ++ return false; ++ } ++ return File::Exists(namespc, path.get()); ++} ++ ++bool File::Create(Namespace* namespc, const char* name, bool exclusive) { ++ NamespaceScope ns(namespc, name); ++ int flags = O_RDONLY | O_CREAT | O_CLOEXEC; ++ if (exclusive) { ++ flags |= O_EXCL; ++ } ++ const int fd = TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), flags, 0666)); ++ if (fd < 0) { ++ return false; ++ } ++ // File.create returns a File, so we shouldn't be giving the illusion that the ++ // call has created a file or that a file already exists if there is already ++ // an entity at the same path that is a directory or a link. ++ bool is_file = true; ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstat64(fd, &st)) == 0) { ++ if (S_ISDIR(st.st_mode)) { ++ errno = EISDIR; ++ is_file = false; ++ } else if (S_ISLNK(st.st_mode)) { ++ errno = ENOENT; ++ is_file = false; ++ } ++ } ++ FDUtils::SaveErrorAndClose(fd); ++ return is_file; ++} ++ ++bool File::CreateLink(Namespace* namespc, ++ const char* name, ++ const char* target) { ++ NamespaceScope ns(namespc, name); ++ return NO_RETRY_EXPECTED(symlinkat(target, ns.fd(), ns.path())) == 0; ++} ++ ++bool File::CreatePipe(Namespace* namespc, File** readPipe, File** writePipe) { ++ int pipe_fds[2]; ++ int status = NO_RETRY_EXPECTED(pipe(pipe_fds)); ++ if (status != 0) { ++ return false; ++ } ++ *readPipe = OpenFD(pipe_fds[0]); ++ *writePipe = OpenFD(pipe_fds[1]); ++ return true; ++} ++ ++File::Type File::GetType(Namespace* namespc, ++ const char* name, ++ bool follow_links) { ++ NamespaceScope ns(namespc, name); ++ struct stat64 entry_info; ++ int stat_success; ++ if (follow_links) { ++ stat_success = ++ TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0)); ++ } else { ++ stat_success = TEMP_FAILURE_RETRY( ++ fstatat64(ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW)); ++ } ++ if (stat_success == -1) { ++ return File::kDoesNotExist; ++ } ++ if (S_ISDIR(entry_info.st_mode)) { ++ return File::kIsDirectory; ++ } ++ if (S_ISREG(entry_info.st_mode)) { ++ return File::kIsFile; ++ } ++ if (S_ISLNK(entry_info.st_mode)) { ++ return File::kIsLink; ++ } ++ if (S_ISSOCK(entry_info.st_mode)) { ++ return File::kIsSock; ++ } ++ if (S_ISFIFO(entry_info.st_mode)) { ++ return File::kIsPipe; ++ } ++ return File::kDoesNotExist; ++} ++ ++static void SetErrno(File::Type type) { ++ switch (type) { ++ case File::kIsDirectory: ++ errno = EISDIR; ++ break; ++ case File::kDoesNotExist: ++ errno = ENOENT; ++ break; ++ default: ++ errno = EINVAL; ++ break; ++ } ++} ++ ++static bool CheckTypeAndSetErrno(Namespace* namespc, ++ const char* name, ++ File::Type expected, ++ bool follow_links) { ++ File::Type actual = File::GetType(namespc, name, follow_links); ++ if (actual == expected) { ++ return true; ++ } ++ SetErrno(actual); ++ return false; ++} ++ ++bool File::Delete(Namespace* namespc, const char* name) { ++ File::Type type = File::GetType(namespc, name, true); ++ if (type == kIsFile || type == kIsSock || type == kIsPipe) { ++ NamespaceScope ns(namespc, name); ++ return (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0); ++ } ++ SetErrno(type); ++ return false; ++} ++ ++bool File::DeleteLink(Namespace* namespc, const char* name) { ++ NamespaceScope ns(namespc, name); ++ return CheckTypeAndSetErrno(namespc, name, kIsLink, false) && ++ (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0); ++} ++ ++bool File::Rename(Namespace* namespc, ++ const char* old_path, ++ const char* new_path) { ++ File::Type type = File::GetType(namespc, old_path, true); ++ if (type == kIsFile || type == kIsSock || type == kIsPipe) { ++ NamespaceScope oldns(namespc, old_path); ++ NamespaceScope newns(namespc, new_path); ++ return (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(), ++ newns.path())) == 0); ++ } ++ SetErrno(type); ++ return false; ++} ++ ++bool File::RenameLink(Namespace* namespc, ++ const char* old_path, ++ const char* new_path) { ++ NamespaceScope oldns(namespc, old_path); ++ NamespaceScope newns(namespc, new_path); ++ return CheckTypeAndSetErrno(namespc, old_path, kIsLink, false) && ++ (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(), ++ newns.path())) == 0); ++} ++ ++bool File::Copy(Namespace* namespc, ++ const char* old_path, ++ const char* new_path) { ++ File::Type type = File::GetType(namespc, old_path, true); ++ if (type != kIsFile && type != kIsSock && type != kIsPipe) { ++ SetErrno(type); ++ return false; ++ } ++ NamespaceScope oldns(namespc, old_path); ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstatat64(oldns.fd(), oldns.path(), &st, 0)) != 0) { ++ return false; ++ } ++ const int old_fd = TEMP_FAILURE_RETRY( ++ openat64(oldns.fd(), oldns.path(), O_RDONLY | O_CLOEXEC)); ++ if (old_fd < 0) { ++ return false; ++ } ++ NamespaceScope newns(namespc, new_path); ++ const int new_fd = TEMP_FAILURE_RETRY( ++ openat64(newns.fd(), newns.path(), ++ O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, st.st_mode)); ++ if (new_fd < 0) { ++ close(old_fd); ++ return false; ++ } ++ int64_t offset = 0; ++ intptr_t result = 1; ++ while (result > 0) { ++ // Loop to ensure we copy everything, and not only up to 2GB. ++ result = NO_RETRY_EXPECTED(sendfile64(new_fd, old_fd, &offset, kMaxUint32)); ++ } ++ // From sendfile man pages: ++ // Applications may wish to fall back to read(2)/write(2) in the case ++ // where sendfile() fails with EINVAL or ENOSYS. ++ if ((result < 0) && ((errno == EINVAL) || (errno == ENOSYS))) { ++ const intptr_t kBufferSize = 8 * KB; ++ uint8_t* buffer = reinterpret_cast(malloc(kBufferSize)); ++ while ((result = TEMP_FAILURE_RETRY(read(old_fd, buffer, kBufferSize))) > ++ 0) { ++ int wrote = TEMP_FAILURE_RETRY(write(new_fd, buffer, result)); ++ if (wrote != result) { ++ result = -1; ++ break; ++ } ++ } ++ free(buffer); ++ } ++ int e = errno; ++ close(old_fd); ++ close(new_fd); ++ if (result < 0) { ++ VOID_NO_RETRY_EXPECTED(unlinkat(newns.fd(), newns.path(), 0)); ++ errno = e; ++ return false; ++ } ++ return true; ++} ++ ++static bool StatHelper(Namespace* namespc, ++ const char* name, ++ struct stat64* st) { ++ NamespaceScope ns(namespc, name); ++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), st, 0)) != 0) { ++ return false; ++ } ++ // Signal an error if it's a directory. ++ if (S_ISDIR(st->st_mode)) { ++ errno = EISDIR; ++ return false; ++ } ++ // Otherwise assume the caller knows what it's doing. ++ return true; ++} ++ ++int64_t File::LengthFromPath(Namespace* namespc, const char* name) { ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return -1; ++ } ++ return st.st_size; ++} ++ ++static int64_t TimespecToMilliseconds(const struct timespec& t) { ++ return static_cast(t.tv_sec) * 1000L + ++ static_cast(t.tv_nsec) / 1000000L; ++} ++ ++static void MillisecondsToTimespec(int64_t millis, struct timespec* t) { ++ ASSERT(t != NULL); ++ t->tv_sec = millis / kMillisecondsPerSecond; ++ t->tv_nsec = (millis % kMillisecondsPerSecond) * 1000L; ++} ++ ++void File::Stat(Namespace* namespc, const char* name, int64_t* data) { ++ NamespaceScope ns(namespc, name); ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) { ++ if (S_ISREG(st.st_mode)) { ++ data[kType] = kIsFile; ++ } else if (S_ISDIR(st.st_mode)) { ++ data[kType] = kIsDirectory; ++ } else if (S_ISLNK(st.st_mode)) { ++ data[kType] = kIsLink; ++ } else if (S_ISSOCK(st.st_mode)) { ++ data[kType] = kIsSock; ++ } else if (S_ISFIFO(st.st_mode)) { ++ data[kType] = kIsPipe; ++ } else { ++ data[kType] = kDoesNotExist; ++ } ++ data[kCreatedTime] = TimespecToMilliseconds(st.st_ctim); ++ data[kModifiedTime] = TimespecToMilliseconds(st.st_mtim); ++ data[kAccessedTime] = TimespecToMilliseconds(st.st_atim); ++ data[kMode] = st.st_mode; ++ data[kSize] = st.st_size; ++ } else { ++ data[kType] = kDoesNotExist; ++ } ++} ++ ++time_t File::LastModified(Namespace* namespc, const char* name) { ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return -1; ++ } ++ return st.st_mtime; ++} ++ ++time_t File::LastAccessed(Namespace* namespc, const char* name) { ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return -1; ++ } ++ return st.st_atime; ++} ++ ++bool File::SetLastAccessed(Namespace* namespc, ++ const char* name, ++ int64_t millis) { ++ // First get the current times. ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return false; ++ } ++ ++ // Set the new time: ++ NamespaceScope ns(namespc, name); ++ struct timespec times[2]; ++ MillisecondsToTimespec(millis, ×[0]); ++ times[1] = st.st_mtim; ++ return utimensat(ns.fd(), ns.path(), times, 0) == 0; ++} ++ ++bool File::SetLastModified(Namespace* namespc, ++ const char* name, ++ int64_t millis) { ++ // First get the current times. ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return false; ++ } ++ ++ // Set the new time: ++ NamespaceScope ns(namespc, name); ++ struct timespec times[2]; ++ times[0] = st.st_atim; ++ MillisecondsToTimespec(millis, ×[1]); ++ return utimensat(ns.fd(), ns.path(), times, 0) == 0; ++} ++ ++const char* File::LinkTarget(Namespace* namespc, ++ const char* name, ++ char* dest, ++ int dest_size) { ++ NamespaceScope ns(namespc, name); ++ struct stat64 link_stats; ++ const int status = TEMP_FAILURE_RETRY( ++ fstatat64(ns.fd(), ns.path(), &link_stats, AT_SYMLINK_NOFOLLOW)); ++ if (status != 0) { ++ return NULL; ++ } ++ if (!S_ISLNK(link_stats.st_mode)) { ++ errno = ENOENT; ++ return NULL; ++ } ++ // Don't rely on the link_stats.st_size for the size of the link ++ // target. For some filesystems, e.g. procfs, this value is always ++ // 0. Also the link might have changed before the readlink call. ++ const int kBufferSize = PATH_MAX + 1; ++ char target[kBufferSize]; ++ const int target_size = ++ TEMP_FAILURE_RETRY(readlinkat(ns.fd(), ns.path(), target, kBufferSize)); ++ if (target_size <= 0) { ++ return NULL; ++ } ++ if (dest == NULL) { ++ dest = DartUtils::ScopedCString(target_size + 1); ++ } else { ++ ASSERT(dest_size > 0); ++ if (dest_size <= target_size) { ++ return NULL; ++ } ++ } ++ memmove(dest, target, target_size); ++ dest[target_size] = '\0'; ++ return dest; ++} ++ ++bool File::IsAbsolutePath(const char* pathname) { ++ return (pathname != NULL) && (pathname[0] == '/'); ++} ++ ++intptr_t File::ReadLinkInto(const char* pathname, ++ char* result, ++ size_t result_size) { ++ ASSERT(pathname != NULL); ++ ASSERT(IsAbsolutePath(pathname)); ++ struct stat64 link_stats; ++ if (TEMP_FAILURE_RETRY(lstat64(pathname, &link_stats)) != 0) { ++ return -1; ++ } ++ if (!S_ISLNK(link_stats.st_mode)) { ++ errno = ENOENT; ++ return -1; ++ } ++ size_t target_size = ++ TEMP_FAILURE_RETRY(readlink(pathname, result, result_size)); ++ if (target_size <= 0) { ++ return -1; ++ } ++ // readlink returns non-zero terminated strings. Append. ++ if (target_size < result_size) { ++ result[target_size] = '\0'; ++ target_size++; ++ } ++ return target_size; ++} ++ ++const char* File::ReadLink(const char* pathname) { ++ // Don't rely on the link_stats.st_size for the size of the link ++ // target. For some filesystems, e.g. procfs, this value is always ++ // 0. Also the link might have changed before the readlink call. ++ const int kBufferSize = PATH_MAX + 1; ++ char target[kBufferSize]; ++ size_t target_size = ReadLinkInto(pathname, target, kBufferSize); ++ if (target_size <= 0) { ++ return NULL; ++ } ++ char* target_name = DartUtils::ScopedCString(target_size); ++ ASSERT(target_name != NULL); ++ memmove(target_name, target, target_size); ++ return target_name; ++} ++ ++const char* File::GetCanonicalPath(Namespace* namespc, ++ const char* name, ++ char* dest, ++ int dest_size) { ++ if (name == NULL) { ++ return NULL; ++ } ++ if (!Namespace::IsDefault(namespc)) { ++ // TODO(zra): There is no realpathat(). Also chasing a symlink might result ++ // in a path to something outside of the namespace, so canonicalizing paths ++ // would have to be done carefully. For now, don't do anything. ++ return name; ++ } ++ char* abs_path; ++ if (dest == NULL) { ++ dest = DartUtils::ScopedCString(PATH_MAX + 1); ++ } else { ++ ASSERT(dest_size >= PATH_MAX); ++ } ++ ASSERT(dest != NULL); ++ do { ++ abs_path = realpath(name, dest); ++ } while ((abs_path == NULL) && (errno == EINTR)); ++ ASSERT(abs_path == NULL || IsAbsolutePath(abs_path)); ++ ASSERT(abs_path == NULL || (abs_path == dest)); ++ return abs_path; ++} ++ ++const char* File::PathSeparator() { ++ return "/"; ++} ++ ++const char* File::StringEscapedPathSeparator() { ++ return "/"; ++} ++ ++File::StdioHandleType File::GetStdioHandleType(int fd) { ++ struct stat64 buf; ++ int result = TEMP_FAILURE_RETRY(fstat64(fd, &buf)); ++ if (result == -1) { ++ return kTypeError; ++ } ++ if (S_ISCHR(buf.st_mode)) { ++ return kTerminal; ++ } ++ if (S_ISFIFO(buf.st_mode)) { ++ return kPipe; ++ } ++ if (S_ISSOCK(buf.st_mode)) { ++ return kSocket; ++ } ++ if (S_ISREG(buf.st_mode)) { ++ return kFile; ++ } ++ return kOther; ++} ++ ++File::Identical File::AreIdentical(Namespace* namespc_1, ++ const char* file_1, ++ Namespace* namespc_2, ++ const char* file_2) { ++ struct stat64 file_1_info; ++ struct stat64 file_2_info; ++ int status; ++ { ++ NamespaceScope ns1(namespc_1, file_1); ++ status = TEMP_FAILURE_RETRY( ++ fstatat64(ns1.fd(), ns1.path(), &file_1_info, AT_SYMLINK_NOFOLLOW)); ++ if (status == -1) { ++ return File::kError; ++ } ++ } ++ { ++ NamespaceScope ns2(namespc_2, file_2); ++ status = TEMP_FAILURE_RETRY( ++ fstatat64(ns2.fd(), ns2.path(), &file_2_info, AT_SYMLINK_NOFOLLOW)); ++ if (status == -1) { ++ return File::kError; ++ } ++ } ++ return ((file_1_info.st_ino == file_2_info.st_ino) && ++ (file_1_info.st_dev == file_2_info.st_dev)) ++ ? File::kIdentical ++ : File::kDifferent; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/file_system_watcher_ohos.cc b/runtime/bin/file_system_watcher_ohos.cc +new file mode 100644 +index 00000000000..fa030a251e8 +--- /dev/null ++++ b/runtime/bin/file_system_watcher_ohos.cc +@@ -0,0 +1,149 @@ ++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/file_system_watcher.h" ++ ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/fdutils.h" ++#include "bin/file.h" ++#include "bin/socket.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool FileSystemWatcher::IsSupported() { ++ return true; ++} ++ ++intptr_t FileSystemWatcher::Init() { ++ int id = NO_RETRY_EXPECTED(inotify_init1(IN_CLOEXEC)); ++ if (id < 0) { ++ return -1; ++ } ++ // Some systems dosn't support setting this as non-blocking. Since watching ++ // internals are kept away from the user, we know it's possible to continue, ++ // even if setting non-blocking fails. ++ FDUtils::SetNonBlocking(id); ++ return id; ++} ++ ++void FileSystemWatcher::Close(intptr_t id) { ++ USE(id); ++} ++ ++intptr_t FileSystemWatcher::WatchPath(intptr_t id, ++ Namespace* namespc, ++ const char* path, ++ int events, ++ bool recursive) { ++ int list_events = IN_DELETE_SELF | IN_MOVE_SELF; ++ if ((events & kCreate) != 0) { ++ list_events |= IN_CREATE; ++ } ++ if ((events & kModifyContent) != 0) { ++ list_events |= IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY; ++ } ++ if ((events & kDelete) != 0) { ++ list_events |= IN_DELETE; ++ } ++ if ((events & kMove) != 0) { ++ list_events |= IN_MOVE; ++ } ++ const char* resolved_path = File::GetCanonicalPath(namespc, path); ++ path = resolved_path != NULL ? resolved_path : path; ++ int path_id = NO_RETRY_EXPECTED(inotify_add_watch(id, path, list_events)); ++ if (path_id < 0) { ++ return -1; ++ } ++ return path_id; ++} ++ ++void FileSystemWatcher::UnwatchPath(intptr_t id, intptr_t path_id) { ++ VOID_NO_RETRY_EXPECTED(inotify_rm_watch(id, path_id)); ++} ++ ++intptr_t FileSystemWatcher::GetSocketId(intptr_t id, intptr_t path_id) { ++ USE(path_id); ++ return id; ++} ++ ++static int InotifyEventToMask(struct inotify_event* e) { ++ int mask = 0; ++ if ((e->mask & IN_CLOSE_WRITE) != 0 || (e->mask & IN_MODIFY) != 0) { ++ mask |= FileSystemWatcher::kModifyContent; ++ } ++ if ((e->mask & IN_ATTRIB) != 0) { ++ mask |= FileSystemWatcher::kModifyAttribute; ++ } ++ if ((e->mask & IN_CREATE) != 0) { ++ mask |= FileSystemWatcher::kCreate; ++ } ++ if ((e->mask & IN_MOVE) != 0) { ++ mask |= FileSystemWatcher::kMove; ++ } ++ if ((e->mask & IN_DELETE) != 0) { ++ mask |= FileSystemWatcher::kDelete; ++ } ++ if ((e->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) != 0) { ++ mask |= FileSystemWatcher::kDeleteSelf; ++ } ++ if ((e->mask & IN_ISDIR) != 0) { ++ mask |= FileSystemWatcher::kIsDir; ++ } ++ return mask; ++} ++ ++Dart_Handle FileSystemWatcher::ReadEvents(intptr_t id, intptr_t path_id) { ++ USE(path_id); ++ const intptr_t kEventSize = sizeof(struct inotify_event); ++ const intptr_t kBufferSize = kEventSize + NAME_MAX + 1; ++ uint8_t buffer[kBufferSize]; ++ intptr_t bytes = ++ SocketBase::Read(id, buffer, kBufferSize, SocketBase::kAsync); ++ if (bytes < 0) { ++ return DartUtils::NewDartOSError(); ++ } ++ const intptr_t kMaxCount = bytes / kEventSize; ++ Dart_Handle events = Dart_NewList(kMaxCount); ++ intptr_t offset = 0; ++ intptr_t i = 0; ++ while (offset < bytes) { ++ struct inotify_event* e = ++ reinterpret_cast(buffer + offset); ++ if ((e->mask & IN_IGNORED) == 0) { ++ Dart_Handle event = Dart_NewList(5); ++ int mask = InotifyEventToMask(e); ++ Dart_ListSetAt(event, 0, Dart_NewInteger(mask)); ++ Dart_ListSetAt(event, 1, Dart_NewInteger(e->cookie)); ++ if (e->len > 0) { ++ Dart_Handle name = Dart_NewStringFromUTF8( ++ reinterpret_cast(e->name), strlen(e->name)); ++ if (Dart_IsError(name)) { ++ return name; ++ } ++ Dart_ListSetAt(event, 2, name); ++ } else { ++ Dart_ListSetAt(event, 2, Dart_Null()); ++ } ++ Dart_ListSetAt(event, 3, Dart_NewBoolean((e->mask & IN_MOVED_TO) != 0u)); ++ Dart_ListSetAt(event, 4, Dart_NewInteger(e->wd)); ++ Dart_ListSetAt(events, i, event); ++ i++; ++ } ++ offset += kEventSize + e->len; ++ } ++ ASSERT(offset == bytes); ++ return events; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/io_impl_sources.gni b/runtime/bin/io_impl_sources.gni +index d3ace672e9b..8367282a1ad 100644 +--- a/runtime/bin/io_impl_sources.gni ++++ b/runtime/bin/io_impl_sources.gni +@@ -14,6 +14,8 @@ io_impl_sources = [ + "eventhandler_fuchsia.h", + "eventhandler_linux.cc", + "eventhandler_linux.h", ++ "eventhandler_ohos.cc", ++ "eventhandler_ohos.h", + "eventhandler_macos.cc", + "eventhandler_macos.h", + "eventhandler_win.cc", +@@ -22,6 +24,7 @@ io_impl_sources = [ + "file_system_watcher.h", + "file_system_watcher_fuchsia.cc", + "file_system_watcher_linux.cc", ++ "file_system_watcher_ohos.cc", + "file_system_watcher_macos.cc", + "file_system_watcher_win.cc", + "filter.cc", +@@ -37,6 +40,7 @@ io_impl_sources = [ + "namespace_fuchsia.cc", + "namespace_fuchsia.h", + "namespace_linux.cc", ++ "namespace_ohos.cc", + "namespace_macos.cc", + "namespace_win.cc", + "platform.cc", +@@ -50,6 +54,7 @@ io_impl_sources = [ + "process.h", + "process_fuchsia.cc", + "process_linux.cc", ++ "process_ohos.cc", + "process_macos.cc", + "process_win.cc", + "reference_counting.h", +@@ -63,6 +68,7 @@ io_impl_sources = [ + "security_context.h", + "security_context_fuchsia.cc", + "security_context_linux.cc", ++ "security_context_ohos.cc", + "security_context_macos.cc", + "security_context_win.cc", + "socket.cc", +@@ -80,18 +86,21 @@ io_impl_sources = [ + "socket_base_win.h", + "socket_fuchsia.cc", + "socket_linux.cc", ++ "socket_ohos.cc", + "socket_macos.cc", + "socket_win.cc", + "stdio.cc", + "stdio.h", + "stdio_fuchsia.cc", + "stdio_linux.cc", ++ "stdio_ohos.cc", + "stdio_macos.cc", + "stdio_win.cc", + "sync_socket.cc", + "sync_socket.h", + "sync_socket_fuchsia.cc", + "sync_socket_linux.cc", ++ "sync_socket_ohos.cc", + "sync_socket_macos.cc", + "sync_socket_win.cc", + "typed_data_utils.cc", +diff --git a/runtime/bin/main_options.cc b/runtime/bin/main_options.cc +index 25d484c60c0..7e4ba510d9e 100644 +--- a/runtime/bin/main_options.cc ++++ b/runtime/bin/main_options.cc +@@ -239,7 +239,7 @@ void Options::PrintUsage() { + " use for secure socket connections.\n" + #if defined(DART_HOST_OS_LINUX) || \ + defined(DART_HOST_OS_ANDROID) || \ +- defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + "--namespace=\n" + " The path to a directory that dart:io calls will treat as the root of the\n" + " filesystem.\n" +diff --git a/runtime/bin/namespace_ohos.cc b/runtime/bin/namespace_ohos.cc +new file mode 100644 +index 00000000000..71b68ca618d +--- /dev/null ++++ b/runtime/bin/namespace_ohos.cc +@@ -0,0 +1,159 @@ ++// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/namespace.h" ++ ++#include ++#include ++ ++#include "bin/file.h" ++#include "platform/signal_blocker.h" ++#include "platform/text_buffer.h" ++ ++namespace dart { ++namespace bin { ++ ++class NamespaceImpl { ++ public: ++ explicit NamespaceImpl(intptr_t rootfd) : rootfd_(rootfd), cwd_(strdup("/")) { ++ ASSERT(rootfd_ > 0); ++ cwdfd_ = dup(rootfd_); ++ ASSERT(cwdfd_ > 0); ++ } ++ ++ explicit NamespaceImpl(const char* path) ++ : rootfd_(TEMP_FAILURE_RETRY(open64(path, O_DIRECTORY))), ++ cwd_(strdup("/")) { ++ ASSERT(rootfd_ > 0); ++ cwdfd_ = dup(rootfd_); ++ ASSERT(cwdfd_ > 0); ++ } ++ ++ ~NamespaceImpl() { ++ NO_RETRY_EXPECTED(close(rootfd_)); ++ free(cwd_); ++ NO_RETRY_EXPECTED(close(cwdfd_)); ++ } ++ ++ intptr_t rootfd() const { return rootfd_; } ++ char* cwd() const { return cwd_; } ++ intptr_t cwdfd() const { return cwdfd_; } ++ ++ bool SetCwd(Namespace* namespc, const char* new_path) { ++ NamespaceScope ns(namespc, new_path); ++ const intptr_t new_cwdfd = ++ TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_DIRECTORY)); ++ if (new_cwdfd < 0) { ++ return false; ++ } ++ ++ TextBuffer tbuf(PATH_MAX); ++ if (!File::IsAbsolutePath(new_path)) { ++ tbuf.AddString(cwd_); ++ } ++ tbuf.AddString(File::PathSeparator()); ++ tbuf.AddString(ns.path()); ++ ++ // Normalize it. ++ char result[PATH_MAX]; ++ const intptr_t result_len = ++ File::CleanUnixPath(tbuf.buffer(), result, PATH_MAX); ++ if (result_len < 0) { ++ errno = ENAMETOOLONG; ++ return false; ++ } ++ ++ free(cwd_); ++ cwd_ = strdup(result); ++ close(cwdfd_); ++ cwdfd_ = new_cwdfd; ++ return true; ++ } ++ ++ private: ++ intptr_t rootfd_; // dirfd for the namespace root. ++ char* cwd_; // cwd relative to the namespace. ++ intptr_t cwdfd_; // dirfd for the cwd. ++ ++ DISALLOW_COPY_AND_ASSIGN(NamespaceImpl); ++}; ++ ++Namespace* Namespace::Create(intptr_t namespc) { ++ NamespaceImpl* namespc_impl = NULL; ++ if (namespc != kNone) { ++ namespc_impl = new NamespaceImpl(namespc); ++ } ++ return new Namespace(namespc_impl); ++} ++ ++Namespace* Namespace::Create(const char* path) { ++ return new Namespace(new NamespaceImpl(path)); ++} ++ ++Namespace::~Namespace() { ++ delete namespc_; ++} ++ ++intptr_t Namespace::Default() { ++ return kNone; ++} ++ ++const char* Namespace::GetCurrent(Namespace* namespc) { ++ if (Namespace::IsDefault(namespc)) { ++ char buffer[PATH_MAX]; ++ if (getcwd(buffer, PATH_MAX) == NULL) { ++ return NULL; ++ } ++ return DartUtils::ScopedCopyCString(buffer); ++ } ++ const char* cwd = namespc->namespc()->cwd(); ++ return cwd; ++} ++ ++bool Namespace::SetCurrent(Namespace* namespc, const char* path) { ++ if (Namespace::IsDefault(namespc)) { ++ return (NO_RETRY_EXPECTED(chdir(path)) == 0); ++ } ++ return namespc->namespc()->SetCwd(namespc, path); ++} ++ ++void Namespace::ResolvePath(Namespace* namespc, ++ const char* path, ++ intptr_t* dirfd, ++ const char** resolved_path) { ++ ASSERT(dirfd != NULL); ++ ASSERT(resolved_path != NULL); ++ if (Namespace::IsDefault(namespc)) { ++ *dirfd = AT_FDCWD; ++ *resolved_path = path; ++ return; ++ } ++ if (File::IsAbsolutePath(path)) { ++ *dirfd = namespc->namespc()->rootfd(); ++ if (strcmp(path, File::PathSeparator()) == 0) { ++ // Change "/" to ".". ++ *resolved_path = "."; ++ } else { ++ // Otherwise strip off the leading "/". ++ *resolved_path = &path[1]; ++ } ++ } else { ++ *dirfd = namespc->namespc()->cwdfd(); ++ *resolved_path = path; ++ } ++} ++ ++NamespaceScope::NamespaceScope(Namespace* namespc, const char* path) { ++ Namespace::ResolvePath(namespc, path, &fd_, &path_); ++} ++ ++NamespaceScope::~NamespaceScope() {} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/platform_linux.cc b/runtime/bin/platform_linux.cc +index 4fed598047c..fc53fda3deb 100644 +--- a/runtime/bin/platform_linux.cc ++++ b/runtime/bin/platform_linux.cc +@@ -3,7 +3,7 @@ + // BSD-style license that can be found in the LICENSE file. + + #include "platform/globals.h" +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + + #include "bin/platform.h" + +diff --git a/runtime/bin/process.h b/runtime/bin/process.h +index 9bd547c802b..6f9db260470 100644 +--- a/runtime/bin/process.h ++++ b/runtime/bin/process.h +@@ -350,7 +350,7 @@ class BufferListBase { + }; + + #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || \ +- defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) ++ defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + class BufferList : public BufferListBase { + public: + BufferList() {} +diff --git a/runtime/bin/process_ohos.cc b/runtime/bin/process_ohos.cc +new file mode 100644 +index 00000000000..30c99dedae7 +--- /dev/null ++++ b/runtime/bin/process_ohos.cc +@@ -0,0 +1,1165 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/process.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/dartutils.h" ++#include "bin/directory.h" ++#include "bin/fdutils.h" ++#include "bin/file.h" ++#include "bin/lockers.h" ++#include "bin/reference_counting.h" ++#include "bin/thread.h" ++#include "platform/syslog.h" ++ ++#include "platform/signal_blocker.h" ++#include "platform/utils.h" ++ ++extern char** environ; ++ ++namespace dart { ++namespace bin { ++ ++int Process::global_exit_code_ = 0; ++Mutex* Process::global_exit_code_mutex_ = nullptr; ++Process::ExitHook Process::exit_hook_ = NULL; ++ ++// ProcessInfo is used to map a process id to the file descriptor for ++// the pipe used to communicate the exit code of the process to Dart. ++// ProcessInfo objects are kept in the static singly-linked ++// ProcessInfoList. ++class ProcessInfo { ++ public: ++ ProcessInfo(pid_t pid, intptr_t fd) : pid_(pid), fd_(fd) {} ++ ~ProcessInfo() { ++ int closed = close(fd_); ++ if (closed != 0) { ++ FATAL("Failed to close process exit code pipe"); ++ } ++ } ++ pid_t pid() { return pid_; } ++ intptr_t fd() { return fd_; } ++ ProcessInfo* next() { return next_; } ++ void set_next(ProcessInfo* info) { next_ = info; } ++ ++ private: ++ pid_t pid_; ++ intptr_t fd_; ++ ProcessInfo* next_; ++ ++ DISALLOW_COPY_AND_ASSIGN(ProcessInfo); ++}; ++ ++// Singly-linked list of ProcessInfo objects for all active processes ++// started from Dart. ++class ProcessInfoList { ++ public: ++ static void Init(); ++ static void Cleanup(); ++ ++ static void AddProcess(pid_t pid, intptr_t fd) { ++ MutexLocker locker(mutex_); ++ ProcessInfo* info = new ProcessInfo(pid, fd); ++ info->set_next(active_processes_); ++ active_processes_ = info; ++ } ++ ++ static intptr_t LookupProcessExitFd(pid_t pid) { ++ MutexLocker locker(mutex_); ++ ProcessInfo* current = active_processes_; ++ while (current != NULL) { ++ if (current->pid() == pid) { ++ return current->fd(); ++ } ++ current = current->next(); ++ } ++ return 0; ++ } ++ ++ static void RemoveProcess(pid_t pid) { ++ MutexLocker locker(mutex_); ++ ProcessInfo* prev = NULL; ++ ProcessInfo* current = active_processes_; ++ while (current != NULL) { ++ if (current->pid() == pid) { ++ if (prev == NULL) { ++ active_processes_ = current->next(); ++ } else { ++ prev->set_next(current->next()); ++ } ++ delete current; ++ return; ++ } ++ prev = current; ++ current = current->next(); ++ } ++ } ++ ++ private: ++ // Linked list of ProcessInfo objects for all active processes ++ // started from Dart code. ++ static ProcessInfo* active_processes_; ++ // Mutex protecting all accesses to the linked list of active ++ // processes. ++ static Mutex* mutex_; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessInfoList); ++}; ++ ++ProcessInfo* ProcessInfoList::active_processes_ = NULL; ++Mutex* ProcessInfoList::mutex_ = nullptr; ++ ++// The exit code handler sets up a separate thread which waits for child ++// processes to terminate. That separate thread can then get the exit code from ++// processes that have exited and communicate it to Dart through the ++// event loop. ++class ExitCodeHandler { ++ public: ++ static void Init(); ++ static void Cleanup(); ++ ++ // Notify the ExitCodeHandler that another process exists. ++ static void ProcessStarted() { ++ // Multiple isolates could be starting processes at the same ++ // time. Make sure that only one ExitCodeHandler thread exists. ++ MonitorLocker locker(monitor_); ++ process_count_++; ++ ++ monitor_->Notify(); ++ ++ if (running_) { ++ return; ++ } ++ ++ // Start thread that handles process exits when wait returns. ++ int result = ++ Thread::Start("dart:io Process.start", ExitCodeHandlerEntry, 0); ++ if (result != 0) { ++ FATAL("Failed to start exit code handler worker thread %d", result); ++ } ++ ++ running_ = true; ++ } ++ ++ static void TerminateExitCodeThread() { ++ MonitorLocker locker(monitor_); ++ ++ if (!running_) { ++ return; ++ } ++ ++ // Set terminate_done_ to false, so we can use it as a guard for our ++ // monitor. ++ running_ = false; ++ ++ // Wake up the [ExitCodeHandler] thread which is blocked on `wait()` (see ++ // [ExitCodeHandlerEntry]). ++ if (TEMP_FAILURE_RETRY(fork()) == 0) { ++ // Avoid calling any atexit callbacks to prevent deadlocks. ++ _exit(0); ++ } ++ ++ monitor_->Notify(); ++ ++ while (!terminate_done_) { ++ monitor_->Wait(Monitor::kNoTimeout); ++ } ++ } ++ ++ private: ++ // Entry point for the separate exit code handler thread started by ++ // the ExitCodeHandler. ++ static void ExitCodeHandlerEntry(uword param) { ++ pid_t pid = 0; ++ int status = 0; ++ while (true) { ++ { ++ MonitorLocker locker(monitor_); ++ while (running_ && process_count_ == 0) { ++ monitor_->Wait(Monitor::kNoTimeout); ++ } ++ if (!running_) { ++ terminate_done_ = true; ++ monitor_->Notify(); ++ return; ++ } ++ } ++ ++ if ((pid = TEMP_FAILURE_RETRY(wait(&status))) > 0) { ++ int exit_code = 0; ++ int negative = 0; ++ if (WIFEXITED(status)) { ++ exit_code = WEXITSTATUS(status); ++ } ++ if (WIFSIGNALED(status)) { ++ exit_code = WTERMSIG(status); ++ negative = 1; ++ } ++ intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid); ++ if (exit_code_fd != 0) { ++ int message[2] = {exit_code, negative}; ++ ssize_t result = ++ FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message)); ++ // If the process has been closed, the read end of the exit ++ // pipe has been closed. It is therefore not a problem that ++ // write fails with a broken pipe error. Other errors should ++ // not happen. ++ if ((result != -1) && (result != sizeof(message))) { ++ FATAL("Failed to write entire process exit message"); ++ } else if ((result == -1) && (errno != EPIPE)) { ++ FATAL("Failed to write exit code: %d", errno); ++ } ++ ProcessInfoList::RemoveProcess(pid); ++ { ++ MonitorLocker locker(monitor_); ++ process_count_--; ++ } ++ } ++ } else if (pid < 0) { ++ FATAL("Wait for process exit failed: %d", errno); ++ } ++ } ++ } ++ ++ static bool terminate_done_; ++ static int process_count_; ++ static bool running_; ++ static Monitor* monitor_; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_IMPLICIT_CONSTRUCTORS(ExitCodeHandler); ++}; ++ ++bool ExitCodeHandler::running_ = false; ++int ExitCodeHandler::process_count_ = 0; ++bool ExitCodeHandler::terminate_done_ = false; ++Monitor* ExitCodeHandler::monitor_ = nullptr; ++ ++class ProcessStarter { ++ public: ++ ProcessStarter(Namespace* namespc, ++ const char* path, ++ char* arguments[], ++ intptr_t arguments_length, ++ const char* working_directory, ++ char* environment[], ++ intptr_t environment_length, ++ ProcessStartMode mode, ++ intptr_t* in, ++ intptr_t* out, ++ intptr_t* err, ++ intptr_t* id, ++ intptr_t* exit_event, ++ char** os_error_message) ++ : namespc_(namespc), ++ path_(path), ++ working_directory_(working_directory), ++ mode_(mode), ++ in_(in), ++ out_(out), ++ err_(err), ++ id_(id), ++ exit_event_(exit_event), ++ os_error_message_(os_error_message) { ++ read_in_[0] = -1; ++ read_in_[1] = -1; ++ read_err_[0] = -1; ++ read_err_[1] = -1; ++ write_out_[0] = -1; ++ write_out_[1] = -1; ++ exec_control_[0] = -1; ++ exec_control_[1] = -1; ++ ++ program_arguments_ = reinterpret_cast(Dart_ScopeAllocate( ++ (arguments_length + 2) * sizeof(*program_arguments_))); ++ program_arguments_[0] = const_cast(path_); ++ for (int i = 0; i < arguments_length; i++) { ++ program_arguments_[i + 1] = arguments[i]; ++ } ++ program_arguments_[arguments_length + 1] = NULL; ++ ++ program_environment_ = NULL; ++ if (environment != NULL) { ++ program_environment_ = reinterpret_cast(Dart_ScopeAllocate( ++ (environment_length + 1) * sizeof(*program_environment_))); ++ for (int i = 0; i < environment_length; i++) { ++ program_environment_[i] = environment[i]; ++ } ++ program_environment_[environment_length] = NULL; ++ } ++ } ++ ++ int Start() { ++ // Create pipes required. ++ int err = CreatePipes(); ++ if (err != 0) { ++ return err; ++ } ++ ++ // Fork to create the new process. ++ pid_t pid = TEMP_FAILURE_RETRY(fork()); ++ if (pid < 0) { ++ // Failed to fork. ++ return CleanupAndReturnError(); ++ } else if (pid == 0) { ++ // This runs in the new process. ++ NewProcess(); ++ } ++ ++ // This runs in the original process. ++ ++ // If the child process is not started in detached mode, be sure to ++ // listen for exit-codes, now that we have a non detached child process ++ // and also Register this child process. ++ if (Process::ModeIsAttached(mode_)) { ++ ExitCodeHandler::ProcessStarted(); ++ err = RegisterProcess(pid); ++ if (err != 0) { ++ return err; ++ } ++ } ++ ++ // Notify child process to start. This is done to delay the call to exec ++ // until the process is registered above, and we are ready to receive the ++ // exit code. ++ char msg = '1'; ++ int bytes_written = ++ FDUtils::WriteToBlocking(read_in_[1], &msg, sizeof(msg)); ++ if (bytes_written != sizeof(msg)) { ++ return CleanupAndReturnError(); ++ } ++ ++ // Read the result of executing the child process. ++ close(exec_control_[1]); ++ exec_control_[1] = -1; ++ if (Process::ModeIsAttached(mode_)) { ++ err = ReadExecResult(); ++ } else { ++ err = ReadDetachedExecResult(&pid); ++ } ++ close(exec_control_[0]); ++ exec_control_[0] = -1; ++ ++ // Return error code if any failures. ++ if (err != 0) { ++ if (Process::ModeIsAttached(mode_)) { ++ // Since exec() failed, we're not interested in the exit code. ++ // We close the reading side of the exit code pipe here. ++ // GetProcessExitCodes will get a broken pipe error when it ++ // tries to write to the writing side of the pipe and it will ++ // ignore the error. ++ close(*exit_event_); ++ *exit_event_ = -1; ++ } ++ CloseAllPipes(); ++ return err; ++ } ++ ++ if (Process::ModeHasStdio(mode_)) { ++ // Connect stdio, stdout and stderr. ++ FDUtils::SetNonBlocking(read_in_[0]); ++ *in_ = read_in_[0]; ++ close(read_in_[1]); ++ FDUtils::SetNonBlocking(write_out_[1]); ++ *out_ = write_out_[1]; ++ close(write_out_[0]); ++ FDUtils::SetNonBlocking(read_err_[0]); ++ *err_ = read_err_[0]; ++ close(read_err_[1]); ++ } else { ++ // Close all fds. ++ close(read_in_[0]); ++ close(read_in_[1]); ++ ASSERT(write_out_[0] == -1); ++ ASSERT(write_out_[1] == -1); ++ ASSERT(read_err_[0] == -1); ++ ASSERT(read_err_[1] == -1); ++ } ++ ASSERT(exec_control_[0] == -1); ++ ASSERT(exec_control_[1] == -1); ++ ++ *id_ = pid; ++ return 0; ++ } ++ ++ private: ++ static constexpr int kErrorBufferSize = 1024; ++ ++ int CreatePipes() { ++ int result; ++ result = TEMP_FAILURE_RETRY(pipe2(exec_control_, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ ++ // For a detached process the pipe to connect stdout is still used for ++ // signaling when to do the first fork. ++ result = TEMP_FAILURE_RETRY(pipe2(read_in_, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ ++ // For detached processes the pipe to connect stderr and stdin are not used. ++ if (Process::ModeHasStdio(mode_)) { ++ result = TEMP_FAILURE_RETRY(pipe2(read_err_, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ ++ result = TEMP_FAILURE_RETRY(pipe2(write_out_, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ } ++ ++ return 0; ++ } ++ ++ void NewProcess() { ++ // Wait for parent process before setting up the child process. ++ char msg; ++ int bytes_read = FDUtils::ReadFromBlocking(read_in_[0], &msg, sizeof(msg)); ++ if (bytes_read != sizeof(msg)) { ++ perror("Failed receiving notification message"); ++ _exit(1); ++ } ++ if (Process::ModeIsAttached(mode_)) { ++ ExecProcess(); ++ } else { ++ ExecDetachedProcess(); ++ } ++ } ++ ++ // Tries to find path_ relative to the current namespace unless it should be ++ // searched in the PATH. ++ // The path that should be passed to exec is returned in realpath. ++ // Returns true on success, and false if there was an error that should ++ // be reported to the parent. ++ bool FindPathInNamespace(char* realpath, intptr_t realpath_size) { ++ // Perform a PATH search if there's no slash in the path. ++ if (strchr(path_, '/') == NULL) { ++ // TODO(zra): If there is a non-default namespace, the entries in PATH ++ // should be treated as relative to the namespace. ++ strncpy(realpath, path_, realpath_size); ++ realpath[realpath_size - 1] = '\0'; ++ return true; ++ } ++ NamespaceScope ns(namespc_, path_); ++ const int fd = ++ TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_RDONLY | O_CLOEXEC)); ++ if (fd == -1) { ++ return false; ++ } ++ char procpath[PATH_MAX]; ++ snprintf(procpath, PATH_MAX, "/proc/self/fd/%d", fd); ++ const intptr_t length = ++ TEMP_FAILURE_RETRY(readlink(procpath, realpath, realpath_size)); ++ if (length < 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return false; ++ } ++ realpath[length] = '\0'; ++ FDUtils::SaveErrorAndClose(fd); ++ return true; ++ } ++ ++ void ExecProcess() { ++ if (mode_ == kNormal) { ++ if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ ++ if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ ++ if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ } else { ++ ASSERT(mode_ == kInheritStdio); ++ } ++ ++ if (working_directory_ != NULL && ++ !Directory::SetCurrent(namespc_, working_directory_)) { ++ ReportChildError(); ++ } ++ ++ if (program_environment_ != NULL) { ++ environ = program_environment_; ++ } ++ ++ char realpath[PATH_MAX]; ++ if (!FindPathInNamespace(realpath, PATH_MAX)) { ++ ReportChildError(); ++ } ++ // TODO(dart:io) Test for the existence of execveat, and use it instead. ++ execvp(realpath, const_cast(program_arguments_)); ++ ReportChildError(); ++ } ++ ++ void ExecDetachedProcess() { ++ if (mode_ == kDetached) { ++ ASSERT(write_out_[0] == -1); ++ ASSERT(write_out_[1] == -1); ++ ASSERT(read_err_[0] == -1); ++ ASSERT(read_err_[1] == -1); ++ // For a detached process the pipe to connect stdout is only used for ++ // signaling when to do the first fork. ++ close(read_in_[0]); ++ read_in_[0] = -1; ++ close(read_in_[1]); ++ read_in_[1] = -1; ++ } else { ++ // Don't close any fds if keeping stdio open to the detached process. ++ ASSERT(mode_ == kDetachedWithStdio); ++ } ++ // Fork once more to start a new session. ++ pid_t pid = TEMP_FAILURE_RETRY(fork()); ++ if (pid < 0) { ++ ReportChildError(); ++ } else if (pid == 0) { ++ // Start a new session. ++ if (TEMP_FAILURE_RETRY(setsid()) == -1) { ++ ReportChildError(); ++ } else { ++ // Do a final fork to not be the session leader. ++ pid = TEMP_FAILURE_RETRY(fork()); ++ if (pid < 0) { ++ ReportChildError(); ++ } else if (pid == 0) { ++ if (mode_ == kDetached) { ++ SetupDetached(); ++ } else { ++ SetupDetachedWithStdio(); ++ } ++ ++ if ((working_directory_ != NULL) && ++ !Directory::SetCurrent(namespc_, working_directory_)) { ++ ReportChildError(); ++ } ++ if (program_environment_ != NULL) { ++ environ = program_environment_; ++ } ++ ++ // Report the final PID and do the exec. ++ ReportPid(getpid()); // getpid cannot fail. ++ char realpath[PATH_MAX]; ++ if (!FindPathInNamespace(realpath, PATH_MAX)) { ++ ReportChildError(); ++ } ++ // TODO(dart:io) Test for the existence of execveat, and use it ++ // instead. ++ execvp(realpath, const_cast(program_arguments_)); ++ ReportChildError(); ++ } else { ++ // Exit the intermediate process. Avoid calling any atexit callbacks ++ // to avoid potential issues (e.g. deadlocks). ++ _exit(0); ++ } ++ } ++ } else { ++ // Exit the intermediate process. Avoid calling any atexit callbacks ++ // to avoid potential issues (e.g. deadlocks). ++ _exit(0); ++ } ++ } ++ ++ int RegisterProcess(pid_t pid) { ++ int result; ++ int event_fds[2]; ++ result = TEMP_FAILURE_RETRY(pipe2(event_fds, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ ++ ProcessInfoList::AddProcess(pid, event_fds[1]); ++ *exit_event_ = event_fds[0]; ++ FDUtils::SetNonBlocking(event_fds[0]); ++ return 0; ++ } ++ ++ int ReadExecResult() { ++ int child_errno; ++ int bytes_read = -1; ++ // Read exec result from child. If no data is returned the exec was ++ // successful and the exec call closed the pipe. Otherwise the errno ++ // is written to the pipe. ++ bytes_read = FDUtils::ReadFromBlocking(exec_control_[0], &child_errno, ++ sizeof(child_errno)); ++ if (bytes_read == sizeof(child_errno)) { ++ ReadChildError(); ++ return child_errno; ++ } else if (bytes_read == -1) { ++ return errno; ++ } ++ return 0; ++ } ++ ++ int ReadDetachedExecResult(pid_t* pid) { ++ int child_errno; ++ int bytes_read = -1; ++ // Read exec result from child. If only pid data is returned the exec was ++ // successful and the exec call closed the pipe. Otherwise the errno ++ // is written to the pipe as well. ++ int result[2]; ++ bytes_read = ++ FDUtils::ReadFromBlocking(exec_control_[0], result, sizeof(result)); ++ if (bytes_read == sizeof(int)) { ++ *pid = result[0]; ++ } else if (bytes_read == 2 * sizeof(int)) { ++ *pid = result[0]; ++ child_errno = result[1]; ++ ReadChildError(); ++ return child_errno; ++ } else if (bytes_read == -1) { ++ return errno; ++ } ++ return 0; ++ } ++ ++ void SetupDetached() { ++ ASSERT(mode_ == kDetached); ++ ++ // Close all open file descriptors except for exec_control_[1]. ++ int max_fds = sysconf(_SC_OPEN_MAX); ++ if (max_fds == -1) { ++ max_fds = _POSIX_OPEN_MAX; ++ } ++ for (int fd = 0; fd < max_fds; fd++) { ++ if (fd != exec_control_[1]) { ++ close(fd); ++ } ++ } ++ ++ // Re-open stdin, stdout and stderr and connect them to /dev/null. ++ // The loop above should already have closed all of them, so ++ // creating new file descriptors should start at STDIN_FILENO. ++ int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)); ++ if (fd != STDIN_FILENO) { ++ ReportChildError(); ++ } ++ if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDOUT_FILENO)) != ++ STDOUT_FILENO) { ++ ReportChildError(); ++ } ++ if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDERR_FILENO)) != ++ STDERR_FILENO) { ++ ReportChildError(); ++ } ++ } ++ ++ void SetupDetachedWithStdio() { ++ // Close all open file descriptors except for ++ // exec_control_[1], write_out_[0], read_in_[1] and ++ // read_err_[1]. ++ int max_fds = sysconf(_SC_OPEN_MAX); ++ if (max_fds == -1) { ++ max_fds = _POSIX_OPEN_MAX; ++ } ++ for (int fd = 0; fd < max_fds; fd++) { ++ if ((fd != exec_control_[1]) && (fd != write_out_[0]) && ++ (fd != read_in_[1]) && (fd != read_err_[1])) { ++ close(fd); ++ } ++ } ++ ++ if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ close(write_out_[0]); ++ ++ if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ close(read_in_[1]); ++ ++ if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ close(read_err_[1]); ++ } ++ ++ int CleanupAndReturnError() { ++ int actual_errno = errno; ++ // If CleanupAndReturnError is called without an actual errno make ++ // sure to return an error anyway. ++ if (actual_errno == 0) { ++ actual_errno = EPERM; ++ } ++ SetChildOsErrorMessage(); ++ CloseAllPipes(); ++ return actual_errno; ++ } ++ ++ void SetChildOsErrorMessage() { ++ char* error_message = DartUtils::ScopedCString(kErrorBufferSize); ++ Utils::StrError(errno, error_message, kErrorBufferSize); ++ *os_error_message_ = error_message; ++ } ++ ++ void ReportChildError() { ++ // In the case of failure in the child process write the errno and ++ // the OS error message to the exec control pipe and exit. ++ int child_errno = errno; ++ char error_buf[kErrorBufferSize]; ++ char* os_error_message = ++ Utils::StrError(errno, error_buf, kErrorBufferSize); ++ int bytes_written = FDUtils::WriteToBlocking(exec_control_[1], &child_errno, ++ sizeof(child_errno)); ++ if (bytes_written == sizeof(child_errno)) { ++ FDUtils::WriteToBlocking(exec_control_[1], os_error_message, ++ strlen(os_error_message) + 1); ++ } ++ close(exec_control_[1]); ++ ++ // We avoid running through registered atexit() handlers because that is ++ // unnecessary work. It can also cause deadlocks on exit in the forked ++ // process. ++ _exit(1); ++ } ++ ++ void ReportPid(int pid) { ++ // In the case of starting a detached process the actual pid of that process ++ // is communicated using the exec control pipe. ++ int bytes_written = ++ FDUtils::WriteToBlocking(exec_control_[1], &pid, sizeof(pid)); ++ ASSERT(bytes_written == sizeof(int)); ++ USE(bytes_written); ++ } ++ ++ void ReadChildError() { ++ char* message = DartUtils::ScopedCString(kErrorBufferSize); ++ if (message != NULL) { ++ FDUtils::ReadFromBlocking(exec_control_[0], message, kErrorBufferSize); ++ message[kErrorBufferSize - 1] = '\0'; ++ *os_error_message_ = message; ++ } else { ++ // Could not get error message. It will be NULL. ++ ASSERT(*os_error_message_ == NULL); ++ } ++ } ++ ++ void ClosePipe(int* fds) { ++ for (int i = 0; i < 2; i++) { ++ if (fds[i] != -1) { ++ close(fds[i]); ++ fds[i] = -1; ++ } ++ } ++ } ++ ++ void CloseAllPipes() { ++ ClosePipe(exec_control_); ++ ClosePipe(read_in_); ++ ClosePipe(read_err_); ++ ClosePipe(write_out_); ++ } ++ ++ int read_in_[2]; // Pipe for stdout to child process. ++ int read_err_[2]; // Pipe for stderr to child process. ++ int write_out_[2]; // Pipe for stdin to child process. ++ int exec_control_[2]; // Pipe to get the result from exec. ++ ++ char** program_arguments_; ++ char** program_environment_; ++ ++ Namespace* namespc_; ++ const char* path_; ++ const char* working_directory_; ++ ProcessStartMode mode_; ++ intptr_t* in_; ++ intptr_t* out_; ++ intptr_t* err_; ++ intptr_t* id_; ++ intptr_t* exit_event_; ++ char** os_error_message_; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessStarter); ++}; ++ ++int Process::Start(Namespace* namespc, ++ const char* path, ++ char* arguments[], ++ intptr_t arguments_length, ++ const char* working_directory, ++ char* environment[], ++ intptr_t environment_length, ++ ProcessStartMode mode, ++ intptr_t* in, ++ intptr_t* out, ++ intptr_t* err, ++ intptr_t* id, ++ intptr_t* exit_event, ++ char** os_error_message) { ++ ProcessStarter starter(namespc, path, arguments, arguments_length, ++ working_directory, environment, environment_length, ++ mode, in, out, err, id, exit_event, os_error_message); ++ return starter.Start(); ++} ++ ++static bool CloseProcessBuffers(struct pollfd* fds, int alive) { ++ int e = errno; ++ for (int i = 0; i < alive; i++) { ++ close(fds[i].fd); ++ } ++ errno = e; ++ return false; ++} ++ ++bool Process::Wait(intptr_t pid, ++ intptr_t in, ++ intptr_t out, ++ intptr_t err, ++ intptr_t exit_event, ++ ProcessResult* result) { ++ // Close input to the process right away. ++ close(in); ++ ++ // There is no return from this function using Dart_PropagateError ++ // as memory used by the buffer lists is freed through their ++ // destructors. ++ BufferList out_data; ++ BufferList err_data; ++ union { ++ uint8_t bytes[8]; ++ int32_t ints[2]; ++ } exit_code_data; ++ ++ struct pollfd fds[3]; ++ fds[0].fd = out; ++ fds[1].fd = err; ++ fds[2].fd = exit_event; ++ ++ for (int i = 0; i < 3; i++) { ++ fds[i].events = POLLIN; ++ } ++ ++ int alive = 3; ++ while (alive > 0) { ++ // Blocking call waiting for events from the child process. ++ if (TEMP_FAILURE_RETRY(poll(fds, alive, -1)) <= 0) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ ++ // Process incoming data. ++ for (int i = 0; i < alive; i++) { ++ if ((fds[i].revents & (POLLNVAL | POLLERR)) != 0) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ if ((fds[i].revents & POLLIN) != 0) { ++ intptr_t avail = FDUtils::AvailableBytes(fds[i].fd); ++ if (fds[i].fd == out) { ++ if (!out_data.Read(out, avail)) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ } else if (fds[i].fd == err) { ++ if (!err_data.Read(err, avail)) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ } else if (fds[i].fd == exit_event) { ++ if (avail == 8) { ++ intptr_t b = ++ TEMP_FAILURE_RETRY(read(exit_event, exit_code_data.bytes, 8)); ++ if (b != 8) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ } ++ } else { ++ UNREACHABLE(); ++ } ++ } ++ if ((fds[i].revents & POLLHUP) != 0) { ++ // Remove the pollfd from the list of pollfds. ++ close(fds[i].fd); ++ alive--; ++ if (i < alive) { ++ fds[i] = fds[alive]; ++ } ++ // Process the same index again. ++ i--; ++ continue; ++ } ++ } ++ } ++ ++ // All handles closed and all data read. ++ result->set_stdout_data(out_data.GetData()); ++ result->set_stderr_data(err_data.GetData()); ++ DEBUG_ASSERT(out_data.IsEmpty()); ++ DEBUG_ASSERT(err_data.IsEmpty()); ++ ++ // Calculate the exit code. ++ intptr_t exit_code = exit_code_data.ints[0]; ++ intptr_t negative = exit_code_data.ints[1]; ++ if (negative != 0) { ++ exit_code = -exit_code; ++ } ++ result->set_exit_code(exit_code); ++ ++ return true; ++} ++ ++bool Process::Kill(intptr_t id, int signal) { ++ return (TEMP_FAILURE_RETRY(kill(id, signal)) != -1); ++} ++ ++void Process::TerminateExitCodeHandler() { ++ ExitCodeHandler::TerminateExitCodeThread(); ++} ++ ++intptr_t Process::CurrentProcessId() { ++ return static_cast(getpid()); ++} ++ ++static void SaveErrorAndClose(FILE* file) { ++ int actual_errno = errno; ++ fclose(file); ++ errno = actual_errno; ++} ++ ++int64_t Process::CurrentRSS() { ++ // The second value in /proc/self/statm is the current RSS in pages. ++ // It is not possible to use getrusage() because the interested fields are not ++ // implemented by the linux kernel. ++ FILE* statm = fopen("/proc/self/statm", "r"); ++ if (statm == NULL) { ++ return -1; ++ } ++ int64_t current_rss_pages = 0; ++ int matches = fscanf(statm, "%*s%" Pd64 "", ¤t_rss_pages); ++ if (matches != 1) { ++ SaveErrorAndClose(statm); ++ return -1; ++ } ++ fclose(statm); ++ return current_rss_pages * getpagesize(); ++} ++ ++int64_t Process::MaxRSS() { ++ struct rusage usage; ++ usage.ru_maxrss = 0; ++ int r = getrusage(RUSAGE_SELF, &usage); ++ if (r < 0) { ++ return -1; ++ } ++ return usage.ru_maxrss * KB; ++} ++ ++static Mutex* signal_mutex = nullptr; ++static SignalInfo* signal_handlers = NULL; ++static const int kSignalsCount = 7; ++static const int kSignals[kSignalsCount] = { ++ SIGHUP, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGWINCH, ++ SIGQUIT // Allow VMService to listen on SIGQUIT. ++}; ++ ++SignalInfo::~SignalInfo() { ++ close(fd_); ++} ++ ++static void SignalHandler(int signal) { ++ MutexLocker lock(signal_mutex); ++ const SignalInfo* handler = signal_handlers; ++ while (handler != NULL) { ++ if (handler->signal() == signal) { ++ int value = 0; ++ VOID_TEMP_FAILURE_RETRY(write(handler->fd(), &value, 1)); ++ } ++ handler = handler->next(); ++ } ++} ++ ++intptr_t Process::SetSignalHandler(intptr_t signal) { ++ bool found = false; ++ for (int i = 0; i < kSignalsCount; i++) { ++ if (kSignals[i] == signal) { ++ found = true; ++ break; ++ } ++ } ++ if (!found) { ++ return -1; ++ } ++ int fds[2]; ++ if (NO_RETRY_EXPECTED(pipe2(fds, O_CLOEXEC)) != 0) { ++ return -1; ++ } ++ ThreadSignalBlocker blocker(kSignalsCount, kSignals); ++ MutexLocker lock(signal_mutex); ++ SignalInfo* handler = signal_handlers; ++ bool listen = true; ++ sa_handler_t oldact_handler = nullptr; ++ while (handler != NULL) { ++ if (handler->signal() == signal) { ++ oldact_handler = handler->oldact(); ++ listen = false; ++ break; ++ } ++ handler = handler->next(); ++ } ++ if (listen) { ++ struct sigaction act = {}; ++ act.sa_handler = SignalHandler; ++ sigemptyset(&act.sa_mask); ++ for (int i = 0; i < kSignalsCount; i++) { ++ sigaddset(&act.sa_mask, kSignals[i]); ++ } ++ struct sigaction oldact = {}; ++ int status = NO_RETRY_EXPECTED(sigaction(signal, &act, &oldact)); ++ if (status < 0) { ++ int err = errno; ++ close(fds[0]); ++ close(fds[1]); ++ errno = err; ++ return -1; ++ } ++ oldact_handler = oldact.sa_handler; ++ } ++ signal_handlers = ++ new SignalInfo(fds[1], signal, oldact_handler, signal_handlers); ++ return fds[0]; ++} ++ ++void Process::ClearSignalHandler(intptr_t signal, Dart_Port port) { ++ ThreadSignalBlocker blocker(kSignalsCount, kSignals); ++ MutexLocker lock(signal_mutex); ++ SignalInfo* handler = signal_handlers; ++ sa_handler_t oldact_handler = SIG_DFL; ++ bool any_removed = false; ++ bool any_remaining = false; ++ while (handler != NULL) { ++ bool remove = false; ++ if (handler->signal() == signal) { ++ if ((port == ILLEGAL_PORT) || (handler->port() == port)) { ++ if (signal_handlers == handler) { ++ signal_handlers = handler->next(); ++ } ++ handler->Unlink(); ++ remove = true; ++ oldact_handler = handler->oldact(); ++ any_removed = true; ++ } else { ++ any_remaining = true; ++ } ++ } ++ SignalInfo* next = handler->next(); ++ if (remove) { ++ delete handler; ++ } ++ handler = next; ++ } ++ if (any_removed && !any_remaining) { ++ struct sigaction act = {}; ++ act.sa_handler = oldact_handler; ++ VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, NULL)); ++ } ++} ++ ++void Process::ClearSignalHandlerByFd(intptr_t fd, Dart_Port port) { ++ ThreadSignalBlocker blocker(kSignalsCount, kSignals); ++ MutexLocker lock(signal_mutex); ++ SignalInfo* handler = signal_handlers; ++ sa_handler_t oldact_handler = SIG_DFL; ++ bool any_remaining = false; ++ intptr_t signal = -1; ++ while (handler != NULL) { ++ bool remove = false; ++ if (handler->fd() == fd) { ++ if ((port == ILLEGAL_PORT) || (handler->port() == port)) { ++ if (signal_handlers == handler) { ++ signal_handlers = handler->next(); ++ } ++ handler->Unlink(); ++ remove = true; ++ signal = handler->signal(); ++ } else { ++ any_remaining = true; ++ } ++ } ++ SignalInfo* next = handler->next(); ++ if (remove) { ++ delete handler; ++ } ++ handler = next; ++ } ++ if ((signal != -1) && !any_remaining) { ++ struct sigaction act = {}; ++ act.sa_handler = oldact_handler; ++ VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, NULL)); ++ } ++} ++ ++void ProcessInfoList::Init() { ++ active_processes_ = NULL; ++ ASSERT(ProcessInfoList::mutex_ == nullptr); ++ ProcessInfoList::mutex_ = new Mutex(); ++} ++ ++void ProcessInfoList::Cleanup() { ++ ASSERT(ProcessInfoList::mutex_ != nullptr); ++ delete ProcessInfoList::mutex_; ++ ProcessInfoList::mutex_ = nullptr; ++} ++ ++void ExitCodeHandler::Init() { ++ running_ = false; ++ process_count_ = 0; ++ terminate_done_ = false; ++ ASSERT(ExitCodeHandler::monitor_ == nullptr); ++ ExitCodeHandler::monitor_ = new Monitor(); ++} ++ ++void ExitCodeHandler::Cleanup() { ++ ASSERT(ExitCodeHandler::monitor_ != nullptr); ++ delete ExitCodeHandler::monitor_; ++ ExitCodeHandler::monitor_ = nullptr; ++} ++ ++void Process::Init() { ++ ExitCodeHandler::Init(); ++ ProcessInfoList::Init(); ++ ++ ASSERT(signal_mutex == nullptr); ++ signal_mutex = new Mutex(); ++ signal_handlers = NULL; ++ ++ ASSERT(Process::global_exit_code_mutex_ == nullptr); ++ Process::global_exit_code_mutex_ = new Mutex(); ++} ++ ++void Process::Cleanup() { ++ ClearAllSignalHandlers(); ++ ++ ASSERT(signal_mutex != nullptr); ++ delete signal_mutex; ++ signal_mutex = nullptr; ++ ++ ASSERT(Process::global_exit_code_mutex_ != nullptr); ++ delete Process::global_exit_code_mutex_; ++ Process::global_exit_code_mutex_ = nullptr; ++ ++ ProcessInfoList::Cleanup(); ++ ExitCodeHandler::Cleanup(); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/security_context_ohos.cc b/runtime/bin/security_context_ohos.cc +new file mode 100644 +index 00000000000..5d801a5b07b +--- /dev/null ++++ b/runtime/bin/security_context_ohos.cc +@@ -0,0 +1,69 @@ ++// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#if !defined(DART_IO_SECURE_SOCKET_DISABLED) ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/security_context.h" ++ ++#include ++#include ++#include ++ ++#include "bin/directory.h" ++#include "bin/file.h" ++#include "bin/secure_socket_filter.h" ++#include "bin/secure_socket_utils.h" ++#include "platform/syslog.h" ++ ++namespace dart { ++namespace bin { ++ ++// The security context won't necessarily use the compiled-in root certificates, ++// but since there is no way to update the size of the allocation after creating ++// the weak persistent handle, we assume that it will. Note that when the ++// root certs aren't compiled in, |root_certificates_pem_length| is 0. ++const intptr_t SSLCertContext::kApproximateSize = ++ sizeof(SSLCertContext) + root_certificates_pem_length; ++ ++void SSLCertContext::TrustBuiltinRoots() { ++ if (root_certs_file() != NULL) { ++ LoadRootCertFile(root_certs_file()); ++ return; ++ } ++ if (root_certs_cache() != NULL) { ++ LoadRootCertCache(root_certs_cache()); ++ return; ++ } ++ ++ const char* bundle = "/etc/ssl/certs/cacert.pem"; ++ const char* cachedir = "/etc/ssl/certs"; ++ if (File::Exists(NULL, bundle)) { ++ LoadRootCertFile(bundle); ++ return; ++ } ++ ++ if (Directory::Exists(NULL, cachedir) == Directory::EXISTS) { ++ LoadRootCertCache(cachedir); ++ return; ++ } ++} ++ ++void SSLCertContext::RegisterCallbacks(SSL* ssl) { ++ // No callbacks to register for implementations using BoringSSL's built-in ++ // verification mechanism. ++} ++ ++TrustEvaluateHandlerFunc SSLCertContext::GetTrustEvaluateHandler() const { ++ return nullptr; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) ++ ++#endif // !defined(DART_IO_SECURE_SOCKET_DISABLED) +diff --git a/runtime/bin/socket.cc b/runtime/bin/socket.cc +index f61a7372148..adb7f1dc25e 100644 +--- a/runtime/bin/socket.cc ++++ b/runtime/bin/socket.cc +@@ -209,12 +209,12 @@ Dart_Handle ListeningSocketRegistry::CreateUnixDomainBindListen( + return result; + } + +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + // Abstract unix domain socket doesn't exist in file system. + if (File::Exists(namespc, addr.un.sun_path) && path[0] != '@') { + #else + if (File::Exists(namespc, addr.un.sun_path)) { +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + if (unix_domain_sockets_ != nullptr) { + // If there is a socket listening on this file. Ensure + // that it was created with `shared` mode and current `shared` +@@ -286,7 +286,7 @@ bool ListeningSocketRegistry::CloseOneSafe(OSSocket* os_socket, + } + // Unlink the socket file, if os_socket contains unix domain sockets. + if (os_socket->address.addr.sa_family == AF_UNIX) { +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + // If the socket is abstract, which has a path starting with a null byte, + // unlink() is not necessary because the file doesn't exist. + if (os_socket->address.un.sun_path[0] != '\0') { +@@ -294,7 +294,7 @@ bool ListeningSocketRegistry::CloseOneSafe(OSSocket* os_socket, + } + #else + Utils::Unlink(os_socket->address.un.sun_path); +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + // Remove os_socket from unix_domain_sockets_ list. + OSSocket* prev = nullptr; + OSSocket* current = unix_domain_sockets_; +diff --git a/runtime/bin/socket.h b/runtime/bin/socket.h +index 4da25e5b6a5..8592fcd7a77 100644 +--- a/runtime/bin/socket.h ++++ b/runtime/bin/socket.h +@@ -262,7 +262,7 @@ class ListeningSocketRegistry { + const char* path) { + while (current != nullptr) { + ASSERT(current->address.addr.sa_family == AF_UNIX); +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + bool condition; + if (path[0] == '\0') { + condition = current->address.un.sun_path[0] == '\0' && +@@ -280,7 +280,7 @@ class ListeningSocketRegistry { + namespc, path) == File::kIdentical) { + return current; + } +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + current = current->next; + } + return nullptr; +diff --git a/runtime/bin/socket_base.cc b/runtime/bin/socket_base.cc +index 40357d0afaf..087d14aa615 100644 +--- a/runtime/bin/socket_base.cc ++++ b/runtime/bin/socket_base.cc +@@ -130,7 +130,7 @@ void SocketAddress::GetSockAddr(Dart_Handle obj, RawAddr* addr) { + Dart_Handle SocketAddress::GetUnixDomainSockAddr(const char* path, + Namespace* namespc, + RawAddr* addr) { +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + NamespaceScope ns(namespc, path); + path = ns.path(); + bool is_abstract = (path[0] == '@'); +@@ -141,7 +141,7 @@ Dart_Handle SocketAddress::GetUnixDomainSockAddr(const char* path, + // connection will be rejected. + bzero(addr->un.sun_path, sizeof(addr->un.sun_path)); + } +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + if (sizeof(path) > sizeof(addr->un.sun_path)) { + OSError os_error(-1, + "The length of path exceeds the limit. " +@@ -151,12 +151,12 @@ Dart_Handle SocketAddress::GetUnixDomainSockAddr(const char* path, + } + addr->un.sun_family = AF_UNIX; + Utils::SNPrint(addr->un.sun_path, sizeof(addr->un.sun_path), "%s", path); +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + // In case of abstract namespace, transfer the leading '@' into a null byte. + if (is_abstract) { + addr->un.sun_path[0] = '\0'; + } +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + return Dart_Null(); + } + +diff --git a/runtime/bin/socket_base.h b/runtime/bin/socket_base.h +index a72a8b6dcd4..99775010f12 100644 +--- a/runtime/bin/socket_base.h ++++ b/runtime/bin/socket_base.h +@@ -9,7 +9,7 @@ + // Declare the OS-specific types ahead of defining the generic class. + #if defined(DART_HOST_OS_FUCHSIA) + #include "bin/socket_base_fuchsia.h" +-#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + #include "bin/socket_base_linux.h" + #elif defined(DART_HOST_OS_MACOS) + #include "bin/socket_base_macos.h" +@@ -85,7 +85,7 @@ class SocketAddress { + + private: + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + // Unix domain address is only on Linux, Mac OS and Android now. + // unix(7) require sun_path to be 108 bytes on Linux and Android, 104 bytes on + // Mac OS. +@@ -95,7 +95,7 @@ class SocketAddress { + #else + char as_string_[INET6_ADDRSTRLEN]; + #endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- // defined(DART_HOST_OS_ANDROID) ++ // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + RawAddr addr_; + + DISALLOW_COPY_AND_ASSIGN(SocketAddress); +diff --git a/runtime/bin/socket_base_linux.cc b/runtime/bin/socket_base_linux.cc +index 570f03b23d2..f20c49c0d1a 100644 +--- a/runtime/bin/socket_base_linux.cc ++++ b/runtime/bin/socket_base_linux.cc +@@ -3,7 +3,7 @@ + // BSD-style license that can be found in the LICENSE file. + + #include "platform/globals.h" +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + + #include "bin/socket_base.h" + +diff --git a/runtime/bin/socket_base_posix.cc b/runtime/bin/socket_base_posix.cc +index 3838e6d11ee..e10127aa71a 100644 +--- a/runtime/bin/socket_base_posix.cc ++++ b/runtime/bin/socket_base_posix.cc +@@ -3,8 +3,7 @@ + // BSD-style license that can be found in the LICENSE file. + + #include "platform/globals.h" +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \ +- defined(DART_HOST_OS_MACOS) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + #include "bin/socket_base.h" + + #include // NOLINT +@@ -509,4 +508,4 @@ bool SocketBase::SetOption(intptr_t fd, + } // namespace dart + + #endif // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \ +- // defined(DART_HOST_OS_MACOS) ++ // defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/socket_ohos.cc b/runtime/bin/socket_ohos.cc +new file mode 100644 +index 00000000000..0d4386b14c6 +--- /dev/null ++++ b/runtime/bin/socket_ohos.cc +@@ -0,0 +1,280 @@ ++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/socket.h" ++ ++#include // NOLINT ++ ++#include "bin/fdutils.h" ++#include "platform/signal_blocker.h" ++#include "platform/syslog.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++Socket::Socket(intptr_t fd) ++ : ReferenceCounted(), ++ fd_(fd), ++ isolate_port_(Dart_GetMainPortId()), ++ port_(ILLEGAL_PORT), ++ udp_receive_buffer_(NULL) {} ++ ++void Socket::CloseFd() { ++ SetClosedFd(); ++} ++ ++void Socket::SetClosedFd() { ++ fd_ = kClosedFd; ++} ++ ++static intptr_t Create(const RawAddr& addr) { ++ intptr_t fd; ++ intptr_t type = SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC; ++ fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, type, 0)); ++ if (fd < 0) { ++ return -1; ++ } ++ return fd; ++} ++ ++static intptr_t Connect(intptr_t fd, const RawAddr& addr) { ++ intptr_t result = TEMP_FAILURE_RETRY( ++ connect(fd, &addr.addr, SocketAddress::GetAddrLength(addr))); ++ if ((result == 0) || (errno == EINPROGRESS)) { ++ return fd; ++ } ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++} ++ ++intptr_t Socket::CreateConnect(const RawAddr& addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ return Connect(fd, addr); ++} ++ ++intptr_t Socket::CreateUnixDomainConnect(const RawAddr& addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ intptr_t result = TEMP_FAILURE_RETRY(connect( ++ fd, (struct sockaddr*)&addr.un, SocketAddress::GetAddrLength(addr))); ++ if (result == 0 || errno == EAGAIN) { ++ return fd; ++ } ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++} ++ ++intptr_t Socket::CreateBindConnect(const RawAddr& addr, ++ const RawAddr& source_addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ ++ intptr_t result = TEMP_FAILURE_RETRY( ++ bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr))); ++ if (result != 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ return Connect(fd, addr); ++} ++ ++intptr_t Socket::CreateUnixDomainBindConnect(const RawAddr& addr, ++ const RawAddr& source_addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ ++ intptr_t result = TEMP_FAILURE_RETRY( ++ bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr))); ++ if (result != 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ result = TEMP_FAILURE_RETRY(connect(fd, (struct sockaddr*)&addr.un, ++ SocketAddress::GetAddrLength(addr))); ++ if (result == 0 || errno == EAGAIN) { ++ return fd; ++ } ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++} ++ ++intptr_t Socket::CreateBindDatagram(const RawAddr& addr, ++ bool reuseAddress, ++ bool reusePort, ++ int ttl) { ++ intptr_t fd; ++ ++ fd = NO_RETRY_EXPECTED(socket(addr.addr.sa_family, ++ SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, ++ IPPROTO_UDP)); ++ if (fd < 0) { ++ return -1; ++ } ++ ++ if (reuseAddress) { ++ int optval = 1; ++ VOID_NO_RETRY_EXPECTED( ++ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))); ++ } ++ ++ if (reusePort) { ++#ifdef SO_REUSEPORT // Not all Linux versions support this. ++ int optval = 1; ++ int reuse_port_success = ++ setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); ++ // Even if it's defined, we might be running on a kernel ++ // that doesn't support it at runtime. ++ if (reuse_port_success != 0) { ++ if (errno == EINTR) { ++ FATAL("Unexpected EINTR errno"); ++ } ++ const int kBufferSize = 1024; ++ char error_buf[kBufferSize]; ++ Syslog::PrintErr("Dart Socket ERROR: %s:%d: %s.", __FILE__, __LINE__, ++ Utils::StrError(errno, error_buf, kBufferSize)); ++ } ++#else // !defined SO_REUSEPORT ++ Syslog::PrintErr( ++ "Dart Socket ERROR: %s:%d: `reusePort` not available on this Linux " ++ "version.", ++ __FILE__, __LINE__); ++#endif // SO_REUSEPORT ++ } ++ ++ if (!SocketBase::SetMulticastHops(fd, ++ addr.addr.sa_family == AF_INET ++ ? SocketAddress::TYPE_IPV4 ++ : SocketAddress::TYPE_IPV6, ++ ttl)) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ if (NO_RETRY_EXPECTED( ++ bind(fd, &addr.addr, SocketAddress::GetAddrLength(addr))) < 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ return fd; ++} ++ ++intptr_t ServerSocket::CreateBindListen(const RawAddr& addr, ++ intptr_t backlog, ++ bool v6_only) { ++ intptr_t fd; ++ ++ fd = NO_RETRY_EXPECTED( ++ socket(addr.ss.ss_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)); ++ if (fd < 0) { ++ return -1; ++ } ++ ++ int optval = 1; ++ VOID_NO_RETRY_EXPECTED( ++ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))); ++ ++ if (addr.ss.ss_family == AF_INET6) { ++ optval = v6_only ? 1 : 0; ++ VOID_NO_RETRY_EXPECTED( ++ setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval))); ++ } ++ ++ if (NO_RETRY_EXPECTED( ++ bind(fd, &addr.addr, SocketAddress::GetAddrLength(addr))) < 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ // Test for invalid socket port 65535 (some browsers disallow it). ++ if ((SocketAddress::GetAddrPort(addr) == 0) && ++ (SocketBase::GetPort(fd) == 65535)) { ++ // Don't close the socket until we have created a new socket, ensuring ++ // that we do not get the bad port number again. ++ intptr_t new_fd = CreateBindListen(addr, backlog, v6_only); ++ FDUtils::SaveErrorAndClose(fd); ++ return new_fd; ++ } ++ ++ if (NO_RETRY_EXPECTED(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ return fd; ++} ++ ++intptr_t ServerSocket::CreateUnixDomainBindListen(const RawAddr& addr, ++ intptr_t backlog) { ++ intptr_t fd = Create(addr); ++ if (NO_RETRY_EXPECTED(bind(fd, (struct sockaddr*)&addr.un, ++ SocketAddress::GetAddrLength(addr))) < 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ if (NO_RETRY_EXPECTED(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ return fd; ++} ++ ++bool ServerSocket::StartAccept(intptr_t fd) { ++ USE(fd); ++ return true; ++} ++ ++static bool IsTemporaryAcceptError(int error) { ++ // On Linux a number of protocol errors should be treated as EAGAIN. ++ // These are the ones for TCP/IP. ++ return (error == EAGAIN) || (error == ENETDOWN) || (error == EPROTO) || ++ (error == ENOPROTOOPT) || (error == EHOSTDOWN) || (error == ENONET) || ++ (error == EHOSTUNREACH) || (error == EOPNOTSUPP) || ++ (error == ENETUNREACH); ++} ++ ++intptr_t ServerSocket::Accept(intptr_t fd) { ++ intptr_t socket; ++ struct sockaddr clientaddr; ++ socklen_t addrlen = sizeof(clientaddr); ++ socket = TEMP_FAILURE_RETRY(accept(fd, &clientaddr, &addrlen)); ++ if (socket == -1) { ++ if (IsTemporaryAcceptError(errno)) { ++ // We need to signal to the caller that this is actually not an ++ // error. We got woken up from the poll on the listening socket, ++ // but there is no connection ready to be accepted. ++ ASSERT(kTemporaryFailure != -1); ++ socket = kTemporaryFailure; ++ } ++ } else { ++ if (!FDUtils::SetCloseOnExec(socket)) { ++ FDUtils::SaveErrorAndClose(socket); ++ return -1; ++ } ++ if (!FDUtils::SetNonBlocking(socket)) { ++ FDUtils::SaveErrorAndClose(socket); ++ return -1; ++ } ++ } ++ return socket; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/stdio_ohos.cc b/runtime/bin/stdio_ohos.cc +new file mode 100644 +index 00000000000..8dd67b67b45 +--- /dev/null ++++ b/runtime/bin/stdio_ohos.cc +@@ -0,0 +1,139 @@ ++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/stdio.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/fdutils.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool Stdin::ReadByte(intptr_t fd, int* byte) { ++ unsigned char b; ++ ssize_t s = TEMP_FAILURE_RETRY(read(fd, &b, 1)); ++ if (s < 0) { ++ return false; ++ } ++ *byte = (s == 0) ? -1 : b; ++ return true; ++} ++ ++bool Stdin::GetEchoMode(intptr_t fd, bool* enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ *enabled = ((term.c_lflag & ECHO) != 0); ++ return true; ++} ++ ++bool Stdin::SetEchoMode(intptr_t fd, bool enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ if (enabled) { ++ term.c_lflag |= ECHO; ++ } else { ++ term.c_lflag &= ~(ECHO); ++ } ++ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term)); ++ return (status == 0); ++} ++ ++bool Stdin::GetEchoNewlineMode(intptr_t fd, bool* enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ *enabled = ((term.c_lflag & ECHONL) != 0); ++ return true; ++} ++ ++bool Stdin::SetEchoNewlineMode(intptr_t fd, bool enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ if (enabled) { ++ term.c_lflag |= ECHONL; ++ } else { ++ term.c_lflag &= ~(ECHONL); ++ } ++ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term)); ++ return (status == 0); ++} ++ ++bool Stdin::GetLineMode(intptr_t fd, bool* enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ *enabled = ((term.c_lflag & ICANON) != 0); ++ return true; ++} ++ ++bool Stdin::SetLineMode(intptr_t fd, bool enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ if (enabled) { ++ term.c_lflag |= ICANON; ++ } else { ++ term.c_lflag &= ~(ICANON); ++ } ++ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term)); ++ return (status == 0); ++} ++ ++static bool TermIsKnownToSupportAnsi() { ++ const char* term = getenv("TERM"); ++ if (term == NULL) { ++ return false; ++ } ++ ++ return strstr(term, "xterm") != NULL || strstr(term, "screen") != NULL || ++ strstr(term, "rxvt") != NULL; ++} ++ ++bool Stdin::AnsiSupported(intptr_t fd, bool* supported) { ++ *supported = (isatty(fd) != 0) && TermIsKnownToSupportAnsi(); ++ return true; ++} ++ ++bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) { ++ struct winsize w; ++ int status = NO_RETRY_EXPECTED(ioctl(fd, TIOCGWINSZ, &w)); ++ if ((status == 0) && ((w.ws_col != 0) || (w.ws_row != 0))) { ++ size[0] = w.ws_col; ++ size[1] = w.ws_row; ++ return true; ++ } ++ return false; ++} ++ ++bool Stdout::AnsiSupported(intptr_t fd, bool* supported) { ++ *supported = (isatty(fd) != 0) && TermIsKnownToSupportAnsi(); ++ return true; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/sync_socket_ohos.cc b/runtime/bin/sync_socket_ohos.cc +new file mode 100644 +index 00000000000..be15707ba28 +--- /dev/null ++++ b/runtime/bin/sync_socket_ohos.cc +@@ -0,0 +1,92 @@ ++// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/sync_socket.h" ++ ++#include // NOLINT ++ ++#include "bin/fdutils.h" ++#include "bin/socket_base.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool SynchronousSocket::Initialize() { ++ // Nothing to do on Ohos. ++ return true; ++} ++ ++static intptr_t Create(const RawAddr& addr) { ++ intptr_t fd; ++ intptr_t type = SOCK_STREAM | SOCK_CLOEXEC; ++ fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, type, 0)); ++ if (fd < 0) { ++ return -1; ++ } ++ return fd; ++} ++ ++static intptr_t Connect(intptr_t fd, const RawAddr& addr) { ++ intptr_t result = TEMP_FAILURE_RETRY( ++ connect(fd, &addr.addr, SocketAddress::GetAddrLength(addr))); ++ if (result == 0) { ++ return fd; ++ } ++ ASSERT(errno != EINPROGRESS); ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++} ++ ++intptr_t SynchronousSocket::CreateConnect(const RawAddr& addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ return Connect(fd, addr); ++} ++ ++intptr_t SynchronousSocket::Available(intptr_t fd) { ++ return SocketBase::Available(fd); ++} ++ ++intptr_t SynchronousSocket::GetPort(intptr_t fd) { ++ return SocketBase::GetPort(fd); ++} ++ ++SocketAddress* SynchronousSocket::GetRemotePeer(intptr_t fd, intptr_t* port) { ++ return SocketBase::GetRemotePeer(fd, port); ++} ++ ++intptr_t SynchronousSocket::Read(intptr_t fd, ++ void* buffer, ++ intptr_t num_bytes) { ++ return SocketBase::Read(fd, buffer, num_bytes, SocketBase::kSync); ++} ++ ++intptr_t SynchronousSocket::Write(intptr_t fd, ++ const void* buffer, ++ intptr_t num_bytes) { ++ return SocketBase::Write(fd, buffer, num_bytes, SocketBase::kSync); ++} ++ ++void SynchronousSocket::ShutdownRead(intptr_t fd) { ++ VOID_NO_RETRY_EXPECTED(shutdown(fd, SHUT_RD)); ++} ++ ++void SynchronousSocket::ShutdownWrite(intptr_t fd) { ++ VOID_NO_RETRY_EXPECTED(shutdown(fd, SHUT_WR)); ++} ++ ++void SynchronousSocket::Close(intptr_t fd) { ++ return SocketBase::Close(fd); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/thread.h b/runtime/bin/thread.h +index e41e8b620a5..c509f3daf17 100644 +--- a/runtime/bin/thread.h ++++ b/runtime/bin/thread.h +@@ -22,6 +22,8 @@ class Monitor; + #include "bin/thread_fuchsia.h" + #elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) + #include "bin/thread_linux.h" ++#elif defined(DART_HOST_OS_OHOS) ++#include "bin/thread_ohos.h" + #elif defined(DART_HOST_OS_MACOS) + #include "bin/thread_macos.h" + #elif defined(DART_HOST_OS_WINDOWS) +diff --git a/runtime/bin/thread_absl.cc b/runtime/bin/thread_absl.cc +index 23bc5e55a5c..3bb3084b6c6 100644 +--- a/runtime/bin/thread_absl.cc ++++ b/runtime/bin/thread_absl.cc +@@ -71,7 +71,7 @@ static void* ThreadStart(void* data_ptr) { + uword parameter = data->parameter(); + delete data; + +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + // Set the thread name. There is 16 bytes limit on the name (including \0). + // pthread_setname_np ignores names that are too long rather than truncating. + char truncated_name[16]; +diff --git a/runtime/bin/thread_ohos.cc b/runtime/bin/thread_ohos.cc +new file mode 100644 +index 00000000000..33657a4f6c5 +--- /dev/null ++++ b/runtime/bin/thread_ohos.cc +@@ -0,0 +1,280 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL) ++ ++#include "bin/thread.h" ++#include "bin/thread_ohos.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "platform/assert.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++#define VALIDATE_PTHREAD_RESULT(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ FATAL("pthread error: %d (%s)", result, \ ++ Utils::StrError(result, error_buf, kBufferSize)); \ ++ } ++ ++#ifdef DEBUG ++#define RETURN_ON_PTHREAD_FAILURE(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ fprintf(stderr, "%s:%d: pthread error: %d (%s)\n", __FILE__, __LINE__, \ ++ result, Utils::StrError(result, error_buf, kBufferSize)); \ ++ return result; \ ++ } ++#else ++#define RETURN_ON_PTHREAD_FAILURE(result) \ ++ if (result != 0) { \ ++ return result; \ ++ } ++#endif ++ ++static void ComputeTimeSpecMicros(struct timespec* ts, int64_t micros) { ++ int64_t secs = micros / kMicrosecondsPerSecond; ++ int64_t nanos = ++ (micros - (secs * kMicrosecondsPerSecond)) * kNanosecondsPerMicrosecond; ++ int result = clock_gettime(CLOCK_MONOTONIC, ts); ++ ASSERT(result == 0); ++ ts->tv_sec += secs; ++ ts->tv_nsec += nanos; ++ if (ts->tv_nsec >= kNanosecondsPerSecond) { ++ ts->tv_sec += 1; ++ ts->tv_nsec -= kNanosecondsPerSecond; ++ } ++} ++ ++class ThreadStartData { ++ public: ++ ThreadStartData(const char* name, ++ Thread::ThreadStartFunction function, ++ uword parameter) ++ : name_(name), function_(function), parameter_(parameter) {} ++ ++ const char* name() const { return name_; } ++ Thread::ThreadStartFunction function() const { return function_; } ++ uword parameter() const { return parameter_; } ++ ++ private: ++ const char* name_; ++ Thread::ThreadStartFunction function_; ++ uword parameter_; ++ ++ DISALLOW_COPY_AND_ASSIGN(ThreadStartData); ++}; ++ ++// Dispatch to the thread start function provided by the caller. This trampoline ++// is used to ensure that the thread is properly destroyed if the thread just ++// exits. ++static void* ThreadStart(void* data_ptr) { ++ ThreadStartData* data = reinterpret_cast(data_ptr); ++ ++ const char* name = data->name(); ++ Thread::ThreadStartFunction function = data->function(); ++ uword parameter = data->parameter(); ++ delete data; ++ ++ // Set the thread name. There is 16 bytes limit on the name (including \0). ++ // pthread_setname_np ignores names that are too long rather than truncating. ++ char truncated_name[16]; ++ snprintf(truncated_name, sizeof(truncated_name), "%s", name); ++ pthread_setname_np(pthread_self(), truncated_name); ++ ++ // Call the supplied thread start function handing it its parameters. ++ function(parameter); ++ ++ return NULL; ++} ++ ++int Thread::Start(const char* name, ++ ThreadStartFunction function, ++ uword parameter) { ++ pthread_attr_t attr; ++ int result = pthread_attr_init(&attr); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_setstacksize(&attr, Thread::GetMaxStackSize()); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ ThreadStartData* data = new ThreadStartData(name, function, parameter); ++ ++ pthread_t tid; ++ result = pthread_create(&tid, &attr, ThreadStart, data); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_destroy(&attr); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ return 0; ++} ++ ++const ThreadId Thread::kInvalidThreadId = static_cast(0); ++ ++intptr_t Thread::GetMaxStackSize() { ++ const int kStackSize = (128 * kWordSize * KB); ++ return kStackSize; ++} ++ ++ThreadId Thread::GetCurrentThreadId() { ++ return pthread_self(); ++} ++ ++bool Thread::Compare(ThreadId a, ThreadId b) { ++ return (pthread_equal(a, b) != 0); ++} ++ ++Mutex::Mutex() { ++ pthread_mutexattr_t attr; ++ int result = pthread_mutexattr_init(&attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); ++ VALIDATE_PTHREAD_RESULT(result); ++#endif // defined(DEBUG) ++ ++ result = pthread_mutex_init(data_.mutex(), &attr); ++ // Verify that creating a pthread_mutex succeeded. ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_mutexattr_destroy(&attr); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++Mutex::~Mutex() { ++ int result = pthread_mutex_destroy(data_.mutex()); ++ // Verify that the pthread_mutex was destroyed. ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void Mutex::Lock() { ++ int result = pthread_mutex_lock(data_.mutex()); ++ // Specifically check for dead lock to help debugging. ++ ASSERT(result != EDEADLK); ++ ASSERT(result == 0); // Verify no other errors. ++ // TODO(iposva): Do we need to track lock owners? ++} ++ ++bool Mutex::TryLock() { ++ int result = pthread_mutex_trylock(data_.mutex()); ++ // Return false if the lock is busy and locking failed. ++ if (result == EBUSY) { ++ return false; ++ } ++ ASSERT(result == 0); // Verify no other errors. ++ // TODO(iposva): Do we need to track lock owners? ++ return true; ++} ++ ++void Mutex::Unlock() { ++ // TODO(iposva): Do we need to track lock owners? ++ int result = pthread_mutex_unlock(data_.mutex()); ++ // Specifically check for wrong thread unlocking to aid debugging. ++ ASSERT(result != EPERM); ++ ASSERT(result == 0); // Verify no other errors. ++} ++ ++Monitor::Monitor() { ++ pthread_mutexattr_t mutex_attr; ++ int result = pthread_mutexattr_init(&mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ result = pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK); ++ VALIDATE_PTHREAD_RESULT(result); ++#endif // defined(DEBUG) ++ ++ result = pthread_mutex_init(data_.mutex(), &mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_mutexattr_destroy(&mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ pthread_condattr_t cond_attr; ++ result = pthread_condattr_init(&cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_cond_init(data_.cond(), &cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_condattr_destroy(&cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++Monitor::~Monitor() { ++ int result = pthread_mutex_destroy(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_cond_destroy(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void Monitor::Enter() { ++ int result = pthread_mutex_lock(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ // TODO(iposva): Do we need to track lock owners? ++} ++ ++void Monitor::Exit() { ++ // TODO(iposva): Do we need to track lock owners? ++ int result = pthread_mutex_unlock(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++Monitor::WaitResult Monitor::Wait(int64_t millis) { ++ return WaitMicros(millis * kMicrosecondsPerMillisecond); ++} ++ ++Monitor::WaitResult Monitor::WaitMicros(int64_t micros) { ++ // TODO(iposva): Do we need to track lock owners? ++ Monitor::WaitResult retval = kNotified; ++ if (micros == kNoTimeout) { ++ // Wait forever. ++ int result = pthread_cond_wait(data_.cond(), data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ } else { ++ struct timespec ts; ++ ComputeTimeSpecMicros(&ts, micros); ++ int result = pthread_cond_timedwait(data_.cond(), data_.mutex(), &ts); ++ ASSERT((result == 0) || (result == ETIMEDOUT)); ++ if (result == ETIMEDOUT) { ++ retval = kTimedOut; ++ } ++ } ++ return retval; ++} ++ ++void Monitor::Notify() { ++ // TODO(iposva): Do we need to track lock owners? ++ int result = pthread_cond_signal(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void Monitor::NotifyAll() { ++ // TODO(iposva): Do we need to track lock owners? ++ int result = pthread_cond_broadcast(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL) +diff --git a/runtime/bin/thread_ohos.h b/runtime/bin/thread_ohos.h +new file mode 100644 +index 00000000000..c15799d45f7 +--- /dev/null ++++ b/runtime/bin/thread_ohos.h +@@ -0,0 +1,57 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#ifndef RUNTIME_BIN_THREAD_OHOS_H_ ++#define RUNTIME_BIN_THREAD_OHOS_H_ ++ ++#if !defined(RUNTIME_BIN_THREAD_H_) ++#error Do not include thread_ohos.h directly; use thread.h instead. ++#endif ++ ++#include ++ ++#include "platform/assert.h" ++#include "platform/globals.h" ++ ++namespace dart { ++namespace bin { ++ ++typedef pthread_t ThreadId; ++ ++class MutexData { ++ private: ++ MutexData() {} ++ ~MutexData() {} ++ ++ pthread_mutex_t* mutex() { return &mutex_; } ++ ++ pthread_mutex_t mutex_; ++ ++ friend class Mutex; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(MutexData); ++}; ++ ++class MonitorData { ++ private: ++ MonitorData() {} ++ ~MonitorData() {} ++ ++ pthread_mutex_t* mutex() { return &mutex_; } ++ pthread_cond_t* cond() { return &cond_; } ++ ++ pthread_mutex_t mutex_; ++ pthread_cond_t cond_; ++ ++ friend class Monitor; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(MonitorData); ++}; ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // RUNTIME_BIN_THREAD_OHOS_H_ +diff --git a/runtime/bin/utils_ohos.cc b/runtime/bin/utils_ohos.cc +new file mode 100644 +index 00000000000..487e4f30199 +--- /dev/null ++++ b/runtime/bin/utils_ohos.cc +@@ -0,0 +1,113 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/utils.h" ++#include "platform/assert.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++OSError::OSError() : sub_system_(kSystem), code_(0), message_(NULL) { ++ Reload(); ++} ++ ++void OSError::Reload() { ++ SetCodeAndMessage(kSystem, errno); ++} ++ ++void OSError::SetCodeAndMessage(SubSystem sub_system, int code) { ++ set_sub_system(sub_system); ++ set_code(code); ++ if (sub_system == kSystem) { ++ const int kBufferSize = 1024; ++ char error_buf[kBufferSize]; ++ SetMessage(Utils::StrError(code, error_buf, kBufferSize)); ++ } else if (sub_system == kGetAddressInfo) { ++ SetMessage(gai_strerror(code)); ++ } else { ++ UNREACHABLE(); ++ } ++} ++ ++const char* StringUtils::ConsoleStringToUtf8(const char* str, ++ intptr_t len, ++ intptr_t* result_len) { ++ return NULL; ++} ++ ++const char* StringUtils::Utf8ToConsoleString(const char* utf8, ++ intptr_t len, ++ intptr_t* result_len) { ++ return NULL; ++} ++ ++char* StringUtils::ConsoleStringToUtf8(char* str, ++ intptr_t len, ++ intptr_t* result_len) { ++ return NULL; ++} ++ ++char* StringUtils::Utf8ToConsoleString(char* utf8, ++ intptr_t len, ++ intptr_t* result_len) { ++ return NULL; ++} ++ ++bool ShellUtils::GetUtf8Argv(int argc, char** argv) { ++ return false; ++} ++ ++void TimerUtils::InitOnce() {} ++ ++int64_t TimerUtils::GetCurrentMonotonicMillis() { ++ return GetCurrentMonotonicMicros() / 1000; ++} ++ ++int64_t TimerUtils::GetCurrentMonotonicMicros() { ++ struct timespec ts; ++ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { ++ UNREACHABLE(); ++ return 0; ++ } ++ // Convert to microseconds. ++ int64_t result = ts.tv_sec; ++ result *= kMicrosecondsPerSecond; ++ result += (ts.tv_nsec / kNanosecondsPerMicrosecond); ++ return result; ++} ++ ++void TimerUtils::Sleep(int64_t millis) { ++ struct timespec req; // requested. ++ struct timespec rem; // remainder. ++ int64_t micros = millis * kMicrosecondsPerMillisecond; ++ int64_t seconds = micros / kMicrosecondsPerSecond; ++ micros = micros - seconds * kMicrosecondsPerSecond; ++ int64_t nanos = micros * kNanosecondsPerMicrosecond; ++ req.tv_sec = seconds; ++ req.tv_nsec = nanos; ++ while (true) { ++ int r = nanosleep(&req, &rem); ++ if (r == 0) { ++ break; ++ } ++ // We should only ever see an interrupt error. ++ ASSERT(errno == EINTR); ++ // Copy remainder into requested and repeat. ++ req = rem; ++ } ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/virtual_memory_posix.cc b/runtime/bin/virtual_memory_posix.cc +index b7f0eaf872c..5a86a3cd5d1 100644 +--- a/runtime/bin/virtual_memory_posix.cc ++++ b/runtime/bin/virtual_memory_posix.cc +@@ -4,7 +4,7 @@ + + #include "platform/globals.h" + #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \ +- defined(DART_HOST_OS_MACOS) ++ defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + + #include "bin/virtual_memory.h" + +@@ -50,6 +50,12 @@ VirtualMemory* VirtualMemory::Allocate(intptr_t size, + } + #endif // defined(DART_HOST_OS_MACOS) + ++#if defined(DART_HOST_OS_OHOS) ++ if (is_executable) { ++ map_flags |= MAP_JIT_OHOS; ++ } ++#endif // defined(DART_HOST_OS_OHOS) ++ + // Some 64-bit microarchitectures store only the low 32-bits of targets as + // part of indirect branch prediction, predicting that the target's upper bits + // will be same as the call instruction's address. This leads to misprediction +diff --git a/runtime/bin/vmservice_impl.cc b/runtime/bin/vmservice_impl.cc +index 7b7559b8af6..f58bb029064 100644 +--- a/runtime/bin/vmservice_impl.cc ++++ b/runtime/bin/vmservice_impl.cc +@@ -132,6 +132,9 @@ bool VmService::Setup(const char* server_ip, + + Dart_Handle result; + ++ //TODO 临时关闭验证 ++ auth_codes_disabled=true; ++ + // Prepare builtin and its dependent libraries for use to resolve URIs. + // Set up various closures, e.g: printing, timers etc. + // Set up 'package root' for URI resolution. +diff --git a/runtime/lib/ffi_dynamic_library.cc b/runtime/lib/ffi_dynamic_library.cc +index 2c6df520f71..cd9840afcae 100644 +--- a/runtime/lib/ffi_dynamic_library.cc ++++ b/runtime/lib/ffi_dynamic_library.cc +@@ -23,7 +23,7 @@ + #include "vm/zone_text_buffer.h" + + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + #include + #endif + +@@ -175,7 +175,7 @@ DEFINE_NATIVE_ENTRY(Ffi_dl_open, 0, 1) { + + DEFINE_NATIVE_ENTRY(Ffi_dl_processLibrary, 0, 0) { + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + return DynamicLibrary::New(RTLD_DEFAULT, false); + #else + return DynamicLibrary::New(kWindowsDynamicLibraryProcessPtr, false); +@@ -425,7 +425,7 @@ static void* FfiResolveAsset(Thread* const thread, + handle = LoadDynamicLibrary(path.ToCString(), error); + } else if (asset_type.Equals(Symbols::process())) { + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + handle = RTLD_DEFAULT; + #else + handle = kWindowsDynamicLibraryProcessPtr; +diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h +index 29bbb969d1d..5c1d340af81 100644 +--- a/runtime/platform/globals.h ++++ b/runtime/platform/globals.h +@@ -109,6 +109,11 @@ + // Check for Android first, to determine its difference from Linux. + #define DART_HOST_OS_ANDROID 1 + ++#elif defined(DART_TARGET_OS_OHOS) && defined(DART_RUNTIME_OS_OHOS) ++ ++#define DART_HOST_OS_OHOS 1 ++#define MAP_JIT_OHOS 0x1000 ++ + #elif defined(__linux__) || defined(__FreeBSD__) + + // Generic Linux. +@@ -384,7 +389,8 @@ struct simd128_value_t { + + #if !defined(DART_TARGET_OS_ANDROID) && !defined(DART_TARGET_OS_FUCHSIA) && \ + !defined(DART_TARGET_OS_MACOS_IOS) && !defined(DART_TARGET_OS_LINUX) && \ +- !defined(DART_TARGET_OS_MACOS) && !defined(DART_TARGET_OS_WINDOWS) ++ !defined(DART_TARGET_OS_MACOS) && !defined(DART_TARGET_OS_WINDOWS) && \ ++ !defined(DART_TARGET_OS_OHOS) + // No target OS specified; pick the one matching the host OS. + #if defined(DART_HOST_OS_ANDROID) + #define DART_TARGET_OS_ANDROID 1 +@@ -395,6 +401,8 @@ struct simd128_value_t { + #define DART_TARGET_OS_MACOS_IOS 1 + #elif defined(DART_HOST_OS_LINUX) + #define DART_TARGET_OS_LINUX 1 ++#elif defined(DART_HOST_OS_OHOS) ++#define DART_TARGET_OS_OHOS 1 + #elif defined(DART_HOST_OS_MACOS) + #define DART_TARGET_OS_MACOS 1 + #elif defined(DART_HOST_OS_WINDOWS) +@@ -741,6 +749,8 @@ DART_FORCE_INLINE D bit_copy(const S& source) { + #define kHostOperatingSystemName "macos" + #elif defined(DART_HOST_OS_WINDOWS) + #define kHostOperatingSystemName "windows" ++#elif defined(DART_HOST_OS_OHOS) ++#define kHostOperatingSystemName "ohos" + #else + #error Host operating system detection failed. + #endif +@@ -785,6 +795,8 @@ DART_FORCE_INLINE D bit_copy(const S& source) { + #define kTargetOperatingSystemName "android" + #elif defined(DART_TARGET_OS_FUCHSIA) + #define kTargetOperatingSystemName "fuchsia" ++#elif defined(DART_TARGET_OS_OHOS) ++#define kTargetOperatingSystemName "ohos" + #elif defined(DART_TARGET_OS_LINUX) + #define kTargetOperatingSystemName "linux" + #elif defined(DART_TARGET_OS_MACOS_IOS) +diff --git a/runtime/platform/platform_sources.gni b/runtime/platform/platform_sources.gni +index e300478c7ea..2ed478e2ee3 100644 +--- a/runtime/platform/platform_sources.gni ++++ b/runtime/platform/platform_sources.gni +@@ -25,6 +25,7 @@ platform_sources = [ + "syslog_android.cc", + "syslog_fuchsia.cc", + "syslog_linux.cc", ++ "syslog_ohos.cc", + "syslog_macos.cc", + "syslog_win.cc", + "text_buffer.cc", +@@ -40,6 +41,7 @@ platform_sources = [ + "utils_android.cc", + "utils_fuchsia.cc", + "utils_linux.cc", ++ "utils_ohos.cc", + "utils_macos.cc", + "utils_win.cc", + ] +diff --git a/runtime/platform/syslog_ohos.cc b/runtime/platform/syslog_ohos.cc +new file mode 100644 +index 00000000000..139881f7271 +--- /dev/null ++++ b/runtime/platform/syslog_ohos.cc +@@ -0,0 +1,46 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "platform/syslog.h" ++ ++#include // NOLINT ++#include // NOLINT ++ ++namespace dart { ++#define HILOG_LOG_DOMAIN 0x0000 ++#define HILOG_LOG_TAG "Dart" ++ ++void Syslog::VPrint(const char* format, va_list args) { ++ ++ va_list stdio_args; ++ va_copy(stdio_args, args); ++ vprintf(format, stdio_args); ++ fflush(stdout); ++ va_end(stdio_args); ++ ++ va_list log_args; ++ va_copy(log_args, args); ++ ((void)OH_LOG_Print(LOG_APP, LOG_INFO, HILOG_LOG_DOMAIN, \ ++ HILOG_LOG_TAG, format, log_args)); ++ va_end(log_args); ++} ++ ++void Syslog::VPrintErr(const char* format, va_list args) { ++ va_list stdio_args; ++ va_copy(stdio_args, args); ++ vprintf(format, stdio_args); ++ fflush(stdout); ++ va_end(stdio_args); ++ va_list log_args; ++ va_copy(log_args, args); ++ ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, HILOG_LOG_DOMAIN, \ ++ HILOG_LOG_TAG, format, log_args)); ++ va_end(log_args); ++} ++ ++} // namespace dart ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/platform/utils.cc b/runtime/platform/utils.cc +index e7179ed8040..88e016482c1 100644 +--- a/runtime/platform/utils.cc ++++ b/runtime/platform/utils.cc +@@ -8,7 +8,7 @@ + #include "platform/globals.h" + + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + #include + #endif + +@@ -262,7 +262,7 @@ static void GetLastErrorAsString(char** error) { + if (error == nullptr) return; // Nothing to do. + + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + const char* status = dlerror(); + *error = status != nullptr ? strdup(status) : nullptr; + #elif defined(DART_HOST_OS_WINDOWS) +@@ -294,7 +294,7 @@ void* Utils::LoadDynamicLibrary(const char* library_path, char** error) { + void* handle = nullptr; + + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + handle = dlopen(library_path, RTLD_LAZY); + #elif defined(DART_HOST_OS_WINDOWS) + SetLastError(0); // Clear any errors. +@@ -329,7 +329,7 @@ void* Utils::ResolveSymbolInDynamicLibrary(void* library_handle, + void* result = nullptr; + + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + dlerror(); // Clear any errors. + result = dlsym(library_handle, symbol); + // Note: nullptr might be a valid return from dlsym. Must call dlerror +@@ -353,7 +353,7 @@ void Utils::UnloadDynamicLibrary(void* library_handle, char** error) { + bool ok = false; + + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + ok = dlclose(library_handle) == 0; + #elif defined(DART_HOST_OS_WINDOWS) + SetLastError(0); // Clear any errors. +diff --git a/runtime/platform/utils.h b/runtime/platform/utils.h +index 18e83926d7c..250b29a0fb4 100644 +--- a/runtime/platform/utils.h ++++ b/runtime/platform/utils.h +@@ -686,6 +686,8 @@ class Utils { + #include "platform/utils_fuchsia.h" + #elif defined(DART_HOST_OS_LINUX) + #include "platform/utils_linux.h" ++#elif defined(DART_HOST_OS_OHOS) ++#include "platform/utils_ohos.h" + #elif defined(DART_HOST_OS_MACOS) + #include "platform/utils_macos.h" + #elif defined(DART_HOST_OS_WINDOWS) +diff --git a/runtime/platform/utils_ohos.cc b/runtime/platform/utils_ohos.cc +new file mode 100644 +index 00000000000..3c768b4e9e7 +--- /dev/null ++++ b/runtime/platform/utils_ohos.cc +@@ -0,0 +1,54 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "platform/utils.h" ++#include "platform/utils_ohos.h" ++ ++namespace dart { ++ ++char* Utils::StrNDup(const char* s, intptr_t n) { ++ return strndup(s, n); ++} ++ ++char* Utils::StrDup(const char* s) { ++ return strdup(s); ++} ++ ++intptr_t Utils::StrNLen(const char* s, intptr_t n) { ++ return strnlen(s, n); ++} ++ ++int Utils::SNPrint(char* str, size_t size, const char* format, ...) { ++ va_list args; ++ va_start(args, format); ++ int retval = VSNPrint(str, size, format, args); ++ va_end(args); ++ return retval; ++} ++ ++int Utils::VSNPrint(char* str, size_t size, const char* format, va_list args) { ++ MSAN_UNPOISON(str, size); ++ int retval = vsnprintf(str, size, format, args); ++ if (retval < 0) { ++ FATAL("Fatal error in Utils::VSNPrint with format '%s'", format); ++ } ++ return retval; ++} ++ ++int Utils::Close(int fildes) { ++ return close(fildes); ++} ++size_t Utils::Read(int filedes, void* buf, size_t nbyte) { ++ return read(filedes, buf, nbyte); ++} ++int Utils::Unlink(const char* path) { ++ return unlink(path); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/platform/utils_ohos.h b/runtime/platform/utils_ohos.h +new file mode 100644 +index 00000000000..abb486ed2d4 +--- /dev/null ++++ b/runtime/platform/utils_ohos.h +@@ -0,0 +1,56 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#ifndef RUNTIME_PLATFORM_UTILS_OHOS_H_ ++#define RUNTIME_PLATFORM_UTILS_OHOS_H_ ++ ++#if !defined(RUNTIME_PLATFORM_UTILS_H_) ++#error Do not include utils_ohos.h directly; use utils.h instead. ++#endif ++ ++#include // NOLINT ++ ++namespace dart { ++ ++inline uint16_t Utils::HostToBigEndian16(uint16_t value) { ++ return htobe16(value); ++} ++ ++inline uint32_t Utils::HostToBigEndian32(uint32_t value) { ++ return htobe32(value); ++} ++ ++inline uint64_t Utils::HostToBigEndian64(uint64_t value) { ++ return htobe64(value); ++} ++ ++inline uint16_t Utils::HostToLittleEndian16(uint16_t value) { ++ return htole16(value); ++} ++ ++inline uint32_t Utils::HostToLittleEndian32(uint32_t value) { ++ return htole32(value); ++} ++ ++inline uint64_t Utils::HostToLittleEndian64(uint64_t value) { ++ return htole64(value); ++} ++ ++inline char* Utils::StrError(int err, char* buffer, size_t bufsize) { ++#if !defined(__GLIBC__) || \ ++ ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) ++ // Use the XSI version. ++ if (strerror_r(err, buffer, bufsize) != 0) { ++ snprintf(buffer, bufsize, "%s", "strerror_r failed"); ++ } ++ return buffer; ++#else ++ // Use the GNU specific version. ++ return strerror_r(err, buffer, bufsize); ++#endif ++} ++ ++} // namespace dart ++ ++#endif // RUNTIME_PLATFORM_UTILS_OHOS_H_ +diff --git a/runtime/runtime_args.gni b/runtime/runtime_args.gni +index dc10b71b7d6..6a43ccad027 100644 +--- a/runtime/runtime_args.gni ++++ b/runtime/runtime_args.gni +@@ -80,7 +80,7 @@ declare_args() { + # The analyze_snapshot tool is only supported on 64 bit AOT builds that use + # ELF. + build_analyze_snapshot = +- (target_os == "linux" || target_os == "android" || ++ (target_os == "linux" || target_os == "android" || target_os == "ohos" || + target_os == "fuchsia") && + (dart_target_arch == "x64" || dart_target_arch == "arm64" || + dart_target_arch == "riscv64") +diff --git a/runtime/vm/compiler/ffi/abi.cc b/runtime/vm/compiler/ffi/abi.cc +index 66fa5a8d564..243aca8daf3 100644 +--- a/runtime/vm/compiler/ffi/abi.cc ++++ b/runtime/vm/compiler/ffi/abi.cc +@@ -31,7 +31,7 @@ static_assert(offsetof(AbiAlignmentUint64, i) == 8, + "FFI transformation alignment"); + #elif (defined(HOST_ARCH_IA32) && /* NOLINT(whitespace/parens) */ \ + (defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID))) || \ ++ defined(DART_HOST_OS_ANDROID)) || defined(DART_HOST_OS_OHOS)) || \ + (defined(HOST_ARCH_ARM) && defined(DART_HOST_OS_IOS)) + static_assert(offsetof(AbiAlignmentDouble, d) == 4, + "FFI transformation alignment"); +@@ -53,6 +53,8 @@ static_assert(offsetof(AbiAlignmentUint64, i) == 8, + #define DART_TARGET_OS_NAME Fuchsia + #elif defined(DART_TARGET_OS_LINUX) + #define DART_TARGET_OS_NAME Linux ++#elif defined(DART_TARGET_OS_OHOS) ++#define DART_TARGET_OS_NAME Ohos + #elif defined(DART_TARGET_OS_MACOS) + #if DART_TARGET_OS_MACOS_IOS + #define DART_TARGET_OS_NAME IOS +diff --git a/runtime/vm/compiler/ffi/abi.h b/runtime/vm/compiler/ffi/abi.h +index ff917bf55d1..e5161488782 100644 +--- a/runtime/vm/compiler/ffi/abi.h ++++ b/runtime/vm/compiler/ffi/abi.h +@@ -25,6 +25,8 @@ enum class Abi { + kAndroidIA32, + kAndroidX64, + kAndroidRiscv64, ++ kOhosArm, ++ kOhosArm64, + kFuchsiaArm64, + kFuchsiaX64, + kFuchsiaRiscv64, +@@ -51,9 +53,9 @@ const int64_t num_abis = static_cast(Abi::kWindowsX64) + 1; + // - runtime/vm/compiler/frontend/kernel_to_il.cc + static_assert(static_cast(Abi::kAndroidArm) == 0, + "Enum value unexpected."); +-static_assert(static_cast(Abi::kWindowsX64) == 21, ++static_assert(static_cast(Abi::kWindowsX64) == 23, + "Enum value unexpected."); +-static_assert(num_abis == 22, "Enum value unexpected."); ++static_assert(num_abis == 24, "Enum value unexpected."); + + // The target ABI. Defines sizes and alignment of native types. + Abi TargetAbi(); +diff --git a/runtime/vm/compiler/ffi/native_calling_convention.cc b/runtime/vm/compiler/ffi/native_calling_convention.cc +index 3914afe326d..b6da03881fb 100644 +--- a/runtime/vm/compiler/ffi/native_calling_convention.cc ++++ b/runtime/vm/compiler/ffi/native_calling_convention.cc +@@ -37,7 +37,7 @@ static bool SoftFpAbi(bool has_varargs, bool is_result) { + } + #else // !defined(FFI_UNIT_TESTS) + static bool SoftFpAbi(bool has_varargs, bool is_result) { +-#if defined(TARGET_ARCH_ARM) && defined(DART_TARGET_OS_ANDROID) ++#if defined(TARGET_ARCH_ARM) && (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_OHOS)) + return true; + #elif defined(TARGET_ARCH_ARM) + return has_varargs; +diff --git a/runtime/vm/compiler/ffi/native_calling_convention_test.cc b/runtime/vm/compiler/ffi/native_calling_convention_test.cc +index 456a6936465..664642d1f5f 100644 +--- a/runtime/vm/compiler/ffi/native_calling_convention_test.cc ++++ b/runtime/vm/compiler/ffi/native_calling_convention_test.cc +@@ -665,7 +665,7 @@ UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_regress46127) { + RunSignatureTest(Z, "regress46127", arguments, struct_type); + + #if defined(TARGET_ARCH_IA32) && \ +- (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX)) ++ (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX)) || defined(DART_TARGET_OS_OHOS) + // We must count the result pointer passed on the stack as well. + EXPECT_EQ(4, native_calling_convention.StackTopInBytes()); + #else +diff --git a/runtime/vm/cpu_arm.cc b/runtime/vm/cpu_arm.cc +index d40147b2ea0..454ac3e4ff0 100644 +--- a/runtime/vm/cpu_arm.cc ++++ b/runtime/vm/cpu_arm.cc +@@ -54,7 +54,7 @@ DEFINE_FLAG(bool, + "Use integer division instruction if supported"); + + #if defined(TARGET_HOST_MISMATCH) +-#if defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS_IOS) ++#if defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS_IOS) || defined(DART_TARGET_OS_OHOS) + DEFINE_FLAG(bool, sim_use_hardfp, false, "Use the hardfp ABI."); + #else + DEFINE_FLAG(bool, sim_use_hardfp, true, "Use the hardfp ABI."); +@@ -80,7 +80,7 @@ void CPU::FlushICache(uword start, uword size) { + // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sys_icache_invalidate.3.html + #if defined(DART_HOST_OS_IOS) + sys_icache_invalidate(reinterpret_cast(start), size); +-#elif defined(DART_HOST_OS_LINUX) ++#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + char* beg = reinterpret_cast(start); + char* end = reinterpret_cast(start + size); + __builtin___clear_cache(beg, end); +diff --git a/runtime/vm/cpu_arm64.cc b/runtime/vm/cpu_arm64.cc +index b64a2ee9259..b195a8e82e3 100644 +--- a/runtime/vm/cpu_arm64.cc ++++ b/runtime/vm/cpu_arm64.cc +@@ -40,7 +40,7 @@ void CPU::FlushICache(uword start, uword size) { + // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sys_icache_invalidate.3.html + #if defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_IOS) + sys_icache_invalidate(reinterpret_cast(start), size); +-#elif defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#elif defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + char* beg = reinterpret_cast(start); + char* end = reinterpret_cast(start + size); + __builtin___clear_cache(beg, end); +diff --git a/runtime/vm/cpu_riscv.cc b/runtime/vm/cpu_riscv.cc +index e1440a9d2ee..ddabbc4773b 100644 +--- a/runtime/vm/cpu_riscv.cc ++++ b/runtime/vm/cpu_riscv.cc +@@ -36,7 +36,7 @@ void CPU::FlushICache(uword start, uword size) { + + #if defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_IOS) + sys_icache_invalidate(reinterpret_cast(start), size); +-#elif defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#elif defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + char* beg = reinterpret_cast(start); + char* end = reinterpret_cast(start + size); + __builtin___clear_cache(beg, end); +diff --git a/runtime/vm/cpuinfo_ohos.cc b/runtime/vm/cpuinfo_ohos.cc +new file mode 100644 +index 00000000000..e5d90ac5dfb +--- /dev/null ++++ b/runtime/vm/cpuinfo_ohos.cc +@@ -0,0 +1,107 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "vm/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "vm/cpuid.h" ++#include "vm/cpuinfo.h" ++#include "vm/proccpuinfo.h" ++ ++#include "platform/assert.h" ++ ++// As with Windows, on IA32 and X64, we use the cpuid instruction. ++// The analogous instruction is privileged on ARM, so we resort to ++// reading from /proc/cpuinfo. ++ ++namespace dart { ++ ++CpuInfoMethod CpuInfo::method_ = kCpuInfoDefault; ++const char* CpuInfo::fields_[kCpuInfoMax] = {0}; ++ ++void CpuInfo::Init() { ++#if defined(HOST_ARCH_IA32) || defined(HOST_ARCH_X64) ++ fields_[kCpuInfoProcessor] = "vendor_id"; ++ fields_[kCpuInfoModel] = "model name"; ++ fields_[kCpuInfoHardware] = "model name"; ++ fields_[kCpuInfoFeatures] = "flags"; ++ fields_[kCpuInfoArchitecture] = "CPU architecture"; ++ method_ = kCpuInfoCpuId; ++ CpuId::Init(); ++#elif defined(HOST_ARCH_ARM) ++ fields_[kCpuInfoProcessor] = "Processor"; ++ fields_[kCpuInfoModel] = "model name"; ++ fields_[kCpuInfoHardware] = "Hardware"; ++ fields_[kCpuInfoFeatures] = "Features"; ++ fields_[kCpuInfoArchitecture] = "CPU architecture"; ++ method_ = kCpuInfoSystem; ++ ProcCpuInfo::Init(); ++#elif defined(HOST_ARCH_ARM64) ++ fields_[kCpuInfoProcessor] = "Processor"; ++ fields_[kCpuInfoModel] = "CPU implementer"; ++ fields_[kCpuInfoHardware] = "CPU implementer"; ++ fields_[kCpuInfoFeatures] = "Features"; ++ fields_[kCpuInfoArchitecture] = "CPU architecture"; ++ method_ = kCpuInfoSystem; ++ ProcCpuInfo::Init(); ++#elif defined(HOST_ARCH_RISCV32) || defined(HOST_ARCH_RISCV64) ++ // We only rely on the base Linux configuration of IMAFDC, so don't need ++ // dynamic feature detection. ++ method_ = kCpuInfoNone; ++#else ++#error Unrecognized target architecture ++#endif ++} ++ ++void CpuInfo::Cleanup() { ++ if (method_ == kCpuInfoCpuId) { ++ CpuId::Cleanup(); ++ } else if (method_ == kCpuInfoSystem) { ++ ProcCpuInfo::Cleanup(); ++ } else { ++ ASSERT(method_ == kCpuInfoNone); ++ } ++} ++ ++bool CpuInfo::FieldContains(CpuInfoIndices idx, const char* search_string) { ++ if (method_ == kCpuInfoCpuId) { ++ const char* field = CpuId::field(idx); ++ bool contains = (strstr(field, search_string) != NULL); ++ free(const_cast(field)); ++ return contains; ++ } else if (method_ == kCpuInfoSystem) { ++ return ProcCpuInfo::FieldContains(FieldName(idx), search_string); ++ } else { ++ UNREACHABLE(); ++ } ++} ++ ++const char* CpuInfo::ExtractField(CpuInfoIndices idx) { ++ if (method_ == kCpuInfoCpuId) { ++ return CpuId::field(idx); ++ } else if (method_ == kCpuInfoSystem) { ++ return ProcCpuInfo::ExtractField(FieldName(idx)); ++ } else { ++ UNREACHABLE(); ++ } ++} ++ ++bool CpuInfo::HasField(const char* field) { ++ if (method_ == kCpuInfoCpuId) { ++ return (strcmp(field, fields_[kCpuInfoProcessor]) == 0) || ++ (strcmp(field, fields_[kCpuInfoModel]) == 0) || ++ (strcmp(field, fields_[kCpuInfoHardware]) == 0) || ++ (strcmp(field, fields_[kCpuInfoFeatures]) == 0); ++ } else if (method_ == kCpuInfoSystem) { ++ return ProcCpuInfo::HasField(field); ++ } else if (method_ == kCpuInfoNone) { ++ return false; ++ } else { ++ UNREACHABLE(); ++ } ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc +index 7fc13a1de68..bb2d7eb8250 100644 +--- a/runtime/vm/dart.cc ++++ b/runtime/vm/dart.cc +@@ -1071,6 +1071,8 @@ char* Dart::FeaturesString(IsolateGroup* isolate_group, + #endif + #elif defined(DART_TARGET_OS_LINUX) + buffer.AddString(" linux"); ++#elif defined(DART_TARGET_OS_OHOS) ++ buffer.AddString(" ohos"); + #elif defined(DART_TARGET_OS_WINDOWS) + buffer.AddString(" windows"); + #else +diff --git a/runtime/vm/globals.h b/runtime/vm/globals.h +index 7cf67f4e416..69c54277b03 100644 +--- a/runtime/vm/globals.h ++++ b/runtime/vm/globals.h +@@ -116,7 +116,7 @@ const intptr_t kDefaultNewGenSemiMaxSize = (kWordSize <= 4) ? 8 : 16; + #if !defined(DART_DISABLE_TIMELINE) && \ + (defined(DART_ENABLE_TIMELINE) || !defined(PRODUCT) || \ + defined(DART_HOST_OS_FUCHSIA) || defined(DART_TARGET_OS_FUCHSIA) || \ +- defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS)) ++ defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_OHOS)) + #define SUPPORT_TIMELINE 1 + #endif + +diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc +index b738e456eee..9e2081f4c19 100644 +--- a/runtime/vm/image_snapshot.cc ++++ b/runtime/vm/image_snapshot.cc +@@ -1060,7 +1060,7 @@ class DwarfAssemblyStream : public DwarfWriteStream { + #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) + stream_->WriteString(".section __DWARF,__debug_abbrev,regular,debug\n"); + #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + stream_->WriteString(".section .debug_abbrev,\"\"\n"); + #else + UNIMPLEMENTED(); +@@ -1070,7 +1070,7 @@ class DwarfAssemblyStream : public DwarfWriteStream { + #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) + stream_->WriteString(".section __DWARF,__debug_info,regular,debug\n"); + #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + stream_->WriteString(".section .debug_info,\"\"\n"); + #else + UNIMPLEMENTED(); +@@ -1082,7 +1082,7 @@ class DwarfAssemblyStream : public DwarfWriteStream { + #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) + stream_->WriteString(".section __DWARF,__debug_line,regular,debug\n"); + #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + stream_->WriteString(".section .debug_line,\"\"\n"); + #else + UNIMPLEMENTED(); +@@ -1170,7 +1170,7 @@ void AssemblyImageWriter::Finalize() { + } + + #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + // Non-executable stack. + #if defined(TARGET_ARCH_ARM) + assembly_stream_->WriteString(".section .note.GNU-stack,\"\",%progbits\n"); +@@ -1403,7 +1403,7 @@ void AssemblyImageWriter::WriteROData(NonStreamingWriteStream* clustered_stream, + WriteBytes(bytes + last_position, symbol.offset - last_position); + assembly_stream_->Printf("\"%s\":\n", symbol.name); + #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + // Output size and type of the read-only data symbol to the assembly stream. + assembly_stream_->Printf(".size \"%s\", %zu\n", symbol.name, symbol.size); + assembly_stream_->Printf(".type \"%s\", %%object\n", symbol.name); +@@ -1445,7 +1445,7 @@ bool AssemblyImageWriter::EnterSection(ProgramSection section, + current_symbols_ = + new (zone_) ZoneGrowableArray(zone_, 0); + #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + assembly_stream_->WriteString(".section .rodata\n"); + #elif defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) + assembly_stream_->WriteString(".const\n"); +@@ -1502,7 +1502,7 @@ void AssemblyImageWriter::ExitSection(ProgramSection name, + // We should still be in the same section as the last EnterSection. + ASSERT_EQUAL(current_section_label_, SectionLabel(name, vm)); + #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + // Output the size of the section symbol to the assembly stream. + assembly_stream_->Printf(".size %s, %zu\n", SectionSymbol(name, vm), size); + assembly_stream_->Printf(".type %s, %%object\n", SectionSymbol(name, vm)); +@@ -1597,7 +1597,7 @@ void AssemblyImageWriter::AddCodeSymbol(const Code& code, + } + assembly_stream_->Printf("\"%s\":\n", symbol); + #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + // Output the size of the code symbol to the assembly stream. + assembly_stream_->Printf(".size \"%s\", %zu\n", symbol, code.Size()); + assembly_stream_->Printf(".type \"%s\", %%function\n", symbol); +diff --git a/runtime/vm/native_symbol_posix.cc b/runtime/vm/native_symbol_posix.cc +index 458ecbc2929..32edcf8e708 100644 +--- a/runtime/vm/native_symbol_posix.cc ++++ b/runtime/vm/native_symbol_posix.cc +@@ -4,7 +4,7 @@ + + #include "vm/globals.h" + #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || \ +- defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) ++ defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + + #include "platform/memory_sanitizer.h" + #include "vm/native_symbol.h" +diff --git a/runtime/vm/os_ohos.cc b/runtime/vm/os_ohos.cc +new file mode 100644 +index 00000000000..e28cf5f1b9b +--- /dev/null ++++ b/runtime/vm/os_ohos.cc +@@ -0,0 +1,706 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "vm/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "vm/os.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "platform/memory_sanitizer.h" ++#include "platform/utils.h" ++#include "vm/code_comments.h" ++#include "vm/code_observers.h" ++#include "vm/dart.h" ++#include "vm/flags.h" ++#include "vm/image_snapshot.h" ++#include "vm/isolate.h" ++#include "vm/lockers.h" ++#include "vm/os_thread.h" ++#include "vm/timeline.h" ++#include "vm/zone.h" ++ ++#include // NOLINT ++namespace dart { ++ ++// Used to choose between Elf32/Elf64 types based on host archotecture bitsize. ++#if defined(ARCH_IS_64_BIT) ++#define ElfW(Type) Elf64_##Type ++#else ++#define ElfW(Type) Elf32_##Type ++#endif ++ ++// Missing from older versions of . ++#if !defined(EM_RISCV) ++#define EM_RISCV 243 ++#endif ++ ++#ifndef PRODUCT ++ ++DEFINE_FLAG(bool, ++ generate_perf_events_symbols, ++ false, ++ "Generate events symbols for profiling with perf (disables dual " ++ "code mapping)"); ++ ++DEFINE_FLAG(bool, ++ generate_perf_jitdump, ++ false, ++ "Generate jitdump file to use with perf-inject (disables dual code " ++ "mapping)"); ++ ++DECLARE_FLAG(bool, write_protect_code); ++DECLARE_FLAG(bool, write_protect_vm_isolate); ++#if !defined(DART_PRECOMPILED_RUNTIME) ++DECLARE_FLAG(bool, code_comments); ++#endif ++ ++// Linux CodeObservers. ++ ++// Simple perf support: generate /tmp/perf-.map file that maps ++// memory ranges to symbol names for JIT generated code. This allows ++// perf-report to resolve addresses falling into JIT generated code. ++// However perf-annotate does not work in this mode because JIT code ++// is transient and does not exist anymore at the moment when you ++// invoke perf-report. ++class PerfCodeObserver : public CodeObserver { ++ public: ++ PerfCodeObserver() : out_file_(NULL) { ++ Dart_FileOpenCallback file_open = Dart::file_open_callback(); ++ if (file_open == NULL) { ++ return; ++ } ++ intptr_t pid = getpid(); ++ char* filename = OS::SCreate(NULL, "/tmp/perf-%" Pd ".map", pid); ++ out_file_ = (*file_open)(filename, true); ++ free(filename); ++ } ++ ++ ~PerfCodeObserver() { ++ Dart_FileCloseCallback file_close = Dart::file_close_callback(); ++ if ((file_close == NULL) || (out_file_ == NULL)) { ++ return; ++ } ++ (*file_close)(out_file_); ++ } ++ ++ virtual bool IsActive() const { ++ return FLAG_generate_perf_events_symbols && (out_file_ != NULL); ++ } ++ ++ virtual void Notify(const char* name, ++ uword base, ++ uword prologue_offset, ++ uword size, ++ bool optimized, ++ const CodeComments* comments) { ++ Dart_FileWriteCallback file_write = Dart::file_write_callback(); ++ if ((file_write == NULL) || (out_file_ == NULL)) { ++ return; ++ } ++ const char* marker = optimized ? "*" : ""; ++ char* buffer = ++ OS::SCreate(Thread::Current()->zone(), "%" Px " %" Px " %s%s\n", base, ++ size, marker, name); ++ { ++ MutexLocker ml(CodeObservers::mutex()); ++ (*file_write)(buffer, strlen(buffer), out_file_); ++ } ++ } ++ ++ private: ++ void* out_file_; ++ ++ DISALLOW_COPY_AND_ASSIGN(PerfCodeObserver); ++}; ++ ++// Code observer that generates a JITDUMP[1] file that can be interpreted by ++// perf-inject to generate ELF images for JIT generated code objects, which ++// allows both perf-report and perf-annotate to recognize them. ++// ++// Usage: ++// ++// $ perf record -k mono dart --generate-perf-jitdump benchmark.dart ++// $ perf inject -j -i perf.data -o perf.data.jitted ++// $ perf report -i perf.data.jitted ++// ++// [1] see linux/tools/perf/Documentation/jitdump-specification.txt for ++// JITDUMP binary format. ++class JitDumpCodeObserver : public CodeObserver { ++ public: ++ JitDumpCodeObserver() : pid_(getpid()) { ++ char* const filename = OS::SCreate(nullptr, "/tmp/jit-%" Pd ".dump", pid_); ++ const int fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0666); ++ free(filename); ++ ++ if (fd == -1) { ++ return; ++ } ++ ++ // Map JITDUMP file, this mapping will be recorded by perf. This allows ++ // perf-inject to find this file later. ++ const long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int) ++ if (page_size == -1) { ++ close(fd); ++ return; ++ } ++ ++ mapped_ = ++ mmap(nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0); ++ if (mapped_ == nullptr) { ++ close(fd); ++ return; ++ } ++ mapped_size_ = page_size; ++ ++ out_file_ = fdopen(fd, "w+"); ++ if (out_file_ == nullptr) { ++ close(fd); ++ return; ++ } ++ ++ // Buffer the output to avoid high IO overheads - we are going to be ++ // writing all JIT generated code out. ++ setvbuf(out_file_, nullptr, _IOFBF, 2 * MB); ++ ++ // Disable code write protection and vm isolate write protection, because ++ // calling mprotect on the pages filled with JIT generated code objects ++ // confuses perf. ++ FLAG_write_protect_code = false; ++ FLAG_write_protect_vm_isolate = false; ++ ++#if !defined(DART_PRECOMPILED_RUNTIME) ++ // Enable code comments. ++ FLAG_code_comments = true; ++#endif ++ ++ // Write JITDUMP header. ++ WriteHeader(); ++ } ++ ++ ~JitDumpCodeObserver() { ++ if (mapped_ != nullptr) { ++ munmap(mapped_, mapped_size_); ++ mapped_ = nullptr; ++ } ++ ++ if (out_file_ != nullptr) { ++ fclose(out_file_); ++ out_file_ = nullptr; ++ } ++ } ++ ++ virtual bool IsActive() const { ++ return FLAG_generate_perf_jitdump && (out_file_ != nullptr); ++ } ++ ++ virtual void Notify(const char* name, ++ uword base, ++ uword prologue_offset, ++ uword size, ++ bool optimized, ++ const CodeComments* comments) { ++ MutexLocker ml(CodeObservers::mutex()); ++ ++ const char* marker = optimized ? "*" : ""; ++ char* buffer = OS::SCreate(Thread::Current()->zone(), "%s%s", marker, name); ++ const size_t name_length = strlen(buffer); ++ ++ WriteDebugInfo(base, comments); ++ ++ CodeLoadEvent ev; ++ ev.event = BaseEvent::kLoad; ++ ev.size = sizeof(ev) + (name_length + 1) + size; ++ ev.time_stamp = OS::GetCurrentMonotonicTicks(); ++ ev.process_id = getpid(); ++ ev.thread_id = syscall(SYS_gettid); ++ ev.vma = base; ++ ev.code_address = base; ++ ev.code_size = size; ++ ev.code_id = code_id_++; ++ ++ WriteFully(&ev, sizeof(ev)); ++ WriteFully(buffer, name_length + 1); ++ WriteFully(reinterpret_cast(base), size); ++ } ++ ++ private: ++ struct Header { ++ const uint32_t magic = 0x4A695444; ++ const uint32_t version = 1; ++ const uint32_t size = sizeof(Header); ++ uint32_t elf_mach_target; ++ const uint32_t reserved = 0xDEADBEEF; ++ uint32_t process_id; ++ uint64_t time_stamp; ++ const uint64_t flags = 0; ++ }; ++ ++ struct BaseEvent { ++ enum Event { ++ kLoad = 0, ++ kMove = 1, ++ kDebugInfo = 2, ++ kClose = 3, ++ kUnwindingInfo = 4 ++ }; ++ ++ uint32_t event; ++ uint32_t size; ++ uint64_t time_stamp; ++ }; ++ ++ struct CodeLoadEvent : BaseEvent { ++ uint32_t process_id; ++ uint32_t thread_id; ++ uint64_t vma; ++ uint64_t code_address; ++ uint64_t code_size; ++ uint64_t code_id; ++ }; ++ ++ struct DebugInfoEvent : BaseEvent { ++ uint64_t address; ++ uint64_t entry_count; ++ // DebugInfoEntry entries[entry_count_]; ++ }; ++ ++ struct DebugInfoEntry { ++ uint64_t address; ++ int32_t line_number; ++ int32_t column; ++ // Followed by nul-terminated name. ++ }; ++ ++ static uint32_t GetElfMachineArchitecture() { ++#if TARGET_ARCH_IA32 ++ return EM_386; ++#elif TARGET_ARCH_X64 ++ return EM_X86_64; ++#elif TARGET_ARCH_ARM ++ return EM_ARM; ++#elif TARGET_ARCH_ARM64 ++ return EM_AARCH64; ++#elif TARGET_ARCH_RISCV32 || TARGET_ARCH_RISCV64 ++ return EM_RISCV; ++#else ++ UNREACHABLE(); ++ return 0; ++#endif ++ } ++ ++#if ARCH_IS_64_BIT ++ static const int kElfHeaderSize = 0x40; ++#else ++ static const int kElfHeaderSize = 0x34; ++#endif ++ ++ void WriteDebugInfo(uword base, const CodeComments* comments) { ++ if (comments == nullptr || comments->Length() == 0) { ++ return; ++ } ++ ++ // Open the comments file for the given code object. ++ // Note: for some reason we can't emit all comments into a single file ++ // the mapping between PCs and lines goes out of sync (might be ++ // perf-annotate bug). ++ char* comments_file_name = ++ OS::SCreate(nullptr, "/tmp/jit-%" Pd "-%" Pd ".cmts", pid_, code_id_); ++ const intptr_t filename_length = strlen(comments_file_name); ++ FILE* comments_file = fopen(comments_file_name, "w"); ++ setvbuf(comments_file, nullptr, _IOFBF, 2 * MB); ++ ++ // Count the number of DebugInfoEntry we are going to emit: one ++ // per PC. ++ intptr_t entry_count = 0; ++ for (uint64_t i = 0, len = comments->Length(); i < len;) { ++ const intptr_t pc_offset = comments->PCOffsetAt(i); ++ while (i < len && comments->PCOffsetAt(i) == pc_offset) { ++ i++; ++ } ++ entry_count++; ++ } ++ ++ DebugInfoEvent info; ++ info.event = BaseEvent::kDebugInfo; ++ info.time_stamp = OS::GetCurrentMonotonicTicks(); ++ info.address = base; ++ info.entry_count = entry_count; ++ info.size = sizeof(info) + ++ entry_count * (sizeof(DebugInfoEntry) + filename_length + 1); ++ const int32_t padding = Utils::RoundUp(info.size, 8) - info.size; ++ info.size += padding; ++ ++ // Write out DebugInfoEvent record followed by entry_count DebugInfoEntry ++ // records. ++ WriteFully(&info, sizeof(info)); ++ intptr_t line_number = 0; // Line number within comments_file. ++ for (intptr_t i = 0, len = comments->Length(); i < len;) { ++ const intptr_t pc_offset = comments->PCOffsetAt(i); ++ while (i < len && comments->PCOffsetAt(i) == pc_offset) { ++ line_number += WriteLn(comments_file, comments->CommentAt(i)); ++ i++; ++ } ++ DebugInfoEntry entry; ++ entry.address = base + pc_offset + kElfHeaderSize; ++ entry.line_number = line_number; ++ entry.column = 0; ++ WriteFully(&entry, sizeof(entry)); ++ WriteFully(comments_file_name, filename_length + 1); ++ } ++ ++ // Write out the padding. ++ const char padding_bytes[8] = {0}; ++ WriteFully(padding_bytes, padding); ++ ++ fclose(comments_file); ++ free(comments_file_name); ++ } ++ ++ void WriteHeader() { ++ Header header; ++ header.elf_mach_target = GetElfMachineArchitecture(); ++ header.process_id = getpid(); ++ header.time_stamp = OS::GetCurrentTimeMicros(); ++ WriteFully(&header, sizeof(header)); ++ } ++ ++ // Returns number of new-lines written. ++ intptr_t WriteLn(FILE* f, const char* comment) { ++ fputs(comment, f); ++ fputc('\n', f); ++ ++ intptr_t line_count = 1; ++ while ((comment = strstr(comment, "\n")) != nullptr) { ++ line_count++; ++ } ++ return line_count; ++ } ++ ++ void WriteFully(const void* buffer, size_t size) { ++ const char* ptr = static_cast(buffer); ++ while (size > 0) { ++ const size_t written = fwrite(ptr, 1, size, out_file_); ++ if (written == 0) { ++ UNREACHABLE(); ++ break; ++ } ++ size -= written; ++ ptr += written; ++ } ++ } ++ ++ const intptr_t pid_; ++ ++ FILE* out_file_ = nullptr; ++ void* mapped_ = nullptr; ++ long mapped_size_ = 0; // NOLINT(runtime/int) ++ ++ intptr_t code_id_ = 0; ++ ++ DISALLOW_COPY_AND_ASSIGN(JitDumpCodeObserver); ++}; ++ ++#endif // !PRODUCT ++ ++intptr_t OS::ProcessId() { ++ return static_cast(getpid()); ++} ++ ++static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) { ++ time_t seconds = static_cast(seconds_since_epoch); ++ if (seconds != seconds_since_epoch) return false; ++ struct tm* error_code = localtime_r(&seconds, tm_result); ++ return error_code != NULL; ++} ++ ++const char* OS::GetTimeZoneName(int64_t seconds_since_epoch) { ++ tm decomposed; ++ bool succeeded = LocalTime(seconds_since_epoch, &decomposed); ++ // If unsuccessful, return an empty string like V8 does. ++ return (succeeded && (decomposed.tm_zone != NULL)) ? decomposed.tm_zone : ""; ++} ++ ++int OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch) { ++ tm decomposed; ++ bool succeeded = LocalTime(seconds_since_epoch, &decomposed); ++ // Even if the offset was 24 hours it would still easily fit into 32 bits. ++ // If unsuccessful, return zero like V8 does. ++ return succeeded ? static_cast(decomposed.tm_gmtoff) : 0; ++} ++ ++int64_t OS::GetCurrentTimeMillis() { ++ return GetCurrentTimeMicros() / 1000; ++} ++ ++int64_t OS::GetCurrentTimeMicros() { ++ // gettimeofday has microsecond resolution. ++ struct timeval tv; ++ if (gettimeofday(&tv, NULL) < 0) { ++ UNREACHABLE(); ++ return 0; ++ } ++ return (static_cast(tv.tv_sec) * 1000000) + tv.tv_usec; ++} ++ ++int64_t OS::GetCurrentMonotonicTicks() { ++ struct timespec ts; ++ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { ++ UNREACHABLE(); ++ return 0; ++ } ++ // Convert to nanoseconds. ++ int64_t result = ts.tv_sec; ++ result *= kNanosecondsPerSecond; ++ result += ts.tv_nsec; ++ return result; ++} ++ ++int64_t OS::GetCurrentMonotonicFrequency() { ++ return kNanosecondsPerSecond; ++} ++ ++int64_t OS::GetCurrentMonotonicMicros() { ++ int64_t ticks = GetCurrentMonotonicTicks(); ++ ASSERT(GetCurrentMonotonicFrequency() == kNanosecondsPerSecond); ++ return ticks / kNanosecondsPerMicrosecond; ++} ++ ++int64_t OS::GetCurrentThreadCPUMicros() { ++ struct timespec ts; ++ if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != 0) { ++ UNREACHABLE(); ++ return -1; ++ } ++ int64_t result = ts.tv_sec; ++ result *= kMicrosecondsPerSecond; ++ result += (ts.tv_nsec / kNanosecondsPerMicrosecond); ++ return result; ++} ++ ++int64_t OS::GetCurrentMonotonicMicrosForTimeline() { ++#if defined(SUPPORT_TIMELINE) ++ if (Timeline::recorder_discards_clock_values()) return -1; ++ return GetCurrentMonotonicMicros(); ++#else ++ return -1; ++#endif ++} ++ ++// TODO(5411554): May need to hoist these architecture dependent code ++// into a architecture specific file e.g: os_ia32_linux.cc ++intptr_t OS::ActivationFrameAlignment() { ++#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) || \ ++ defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_RISCV32) || \ ++ defined(TARGET_ARCH_RISCV64) ++ const int kMinimumAlignment = 16; ++#elif defined(TARGET_ARCH_ARM) ++ const int kMinimumAlignment = 8; ++#else ++#error Unsupported architecture. ++#endif ++ intptr_t alignment = kMinimumAlignment; ++ // TODO(5411554): Allow overriding default stack alignment for ++ // testing purposes. ++ // Flags::DebugIsInt("stackalign", &alignment); ++ ASSERT(Utils::IsPowerOfTwo(alignment)); ++ ASSERT(alignment >= kMinimumAlignment); ++ return alignment; ++} ++ ++int OS::NumberOfAvailableProcessors() { ++ return sysconf(_SC_NPROCESSORS_ONLN); ++} ++ ++void OS::Sleep(int64_t millis) { ++ int64_t micros = millis * kMicrosecondsPerMillisecond; ++ SleepMicros(micros); ++} ++ ++void OS::SleepMicros(int64_t micros) { ++ struct timespec req; // requested. ++ struct timespec rem; // remainder. ++ int64_t seconds = micros / kMicrosecondsPerSecond; ++ micros = micros - seconds * kMicrosecondsPerSecond; ++ int64_t nanos = micros * kNanosecondsPerMicrosecond; ++ req.tv_sec = seconds; ++ req.tv_nsec = nanos; ++ while (true) { ++ int r = nanosleep(&req, &rem); ++ if (r == 0) { ++ break; ++ } ++ // We should only ever see an interrupt error. ++ ASSERT(errno == EINTR); ++ // Copy remainder into requested and repeat. ++ req = rem; ++ } ++} ++ ++// TODO(regis): Function called only from the simulator. ++void OS::DebugBreak() { ++ __builtin_trap(); ++} ++ ++DART_NOINLINE uintptr_t OS::GetProgramCounter() { ++ return reinterpret_cast( ++ __builtin_extract_return_addr(__builtin_return_address(0))); ++} ++ ++void OS::Print(const char* format, ...) { ++ va_list args; ++ va_start(args, format); ++ ((void)OH_LOG_Print(LOG_APP, LOG_INFO, 0x0000, "DartVM", format, args)); ++ va_end(args); ++} ++ ++void OS::VFPrint(FILE* stream, const char* format, va_list args) { ++ vfprintf(stream, format, args); ++ fflush(stream); ++} ++ ++char* OS::SCreate(Zone* zone, const char* format, ...) { ++ va_list args; ++ va_start(args, format); ++ char* buffer = VSCreate(zone, format, args); ++ va_end(args); ++ return buffer; ++} ++ ++char* OS::VSCreate(Zone* zone, const char* format, va_list args) { ++ // Measure. ++ va_list measure_args; ++ va_copy(measure_args, args); ++ intptr_t len = Utils::VSNPrint(NULL, 0, format, measure_args); ++ va_end(measure_args); ++ ++ char* buffer; ++ if (zone != nullptr) { ++ buffer = zone->Alloc(len + 1); ++ } else { ++ buffer = reinterpret_cast(malloc(len + 1)); ++ } ++ ASSERT(buffer != NULL); ++ ++ // Print. ++ va_list print_args; ++ va_copy(print_args, args); ++ Utils::VSNPrint(buffer, len + 1, format, print_args); ++ va_end(print_args); ++ return buffer; ++} ++ ++bool OS::StringToInt64(const char* str, int64_t* value) { ++ ASSERT(str != NULL && strlen(str) > 0 && value != NULL); ++ int32_t base = 10; ++ char* endptr; ++ int i = 0; ++ if (str[0] == '-') { ++ i = 1; ++ } else if (str[0] == '+') { ++ i = 1; ++ } ++ if ((str[i] == '0') && (str[i + 1] == 'x' || str[i + 1] == 'X') && ++ (str[i + 2] != '\0')) { ++ base = 16; ++ } ++ errno = 0; ++ if (base == 16) { ++ // Unsigned 64-bit hexadecimal integer literals are allowed but ++ // immediately interpreted as signed 64-bit integers. ++ *value = static_cast(strtoull(str, &endptr, base)); ++ } else { ++ *value = strtoll(str, &endptr, base); ++ } ++ return ((errno == 0) && (endptr != str) && (*endptr == 0)); ++} ++ ++void OS::RegisterCodeObservers() { ++#ifndef PRODUCT ++ if (FLAG_generate_perf_events_symbols) { ++ CodeObservers::Register(new PerfCodeObserver); ++ } ++ ++ if (FLAG_generate_perf_jitdump) { ++ CodeObservers::Register(new JitDumpCodeObserver); ++ } ++#endif // !PRODUCT ++} ++ ++void OS::PrintErr(const char* format, ...) { ++ va_list args; ++ va_start(args, format); ++ ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, 0x0000, "DartVM", format, args)); ++ va_end(args); ++} ++ ++void OS::Init() {} ++ ++void OS::Cleanup() {} ++ ++void OS::PrepareToAbort() {} ++ ++void OS::Abort() { ++ PrepareToAbort(); ++ abort(); ++} ++ ++void OS::Exit(int code) { ++ exit(code); ++} ++ ++OS::BuildId OS::GetAppBuildId(const uint8_t* snapshot_instructions) { ++ // First return the build ID information from the instructions image if ++ // available. ++ const Image instructions_image(snapshot_instructions); ++ if (auto* const image_build_id = instructions_image.build_id()) { ++ return {instructions_image.build_id_length(), image_build_id}; ++ } ++ Dl_info snapshot_info; ++ if (dladdr(snapshot_instructions, &snapshot_info) == 0) { ++ return {0, nullptr}; ++ } ++ const uint8_t* dso_base = ++ static_cast(snapshot_info.dli_fbase); ++ const ElfW(Ehdr)& elf_header = *reinterpret_cast(dso_base); ++ const ElfW(Phdr)* const phdr_array = ++ reinterpret_cast(dso_base + elf_header.e_phoff); ++ for (intptr_t i = 0; i < elf_header.e_phnum; i++) { ++ const ElfW(Phdr)& header = phdr_array[i]; ++ if (header.p_type != PT_NOTE) continue; ++ if ((header.p_flags & PF_R) != PF_R) continue; ++ const uint8_t* const note_addr = dso_base + header.p_vaddr; ++ const Elf32_Nhdr& note_header = ++ *reinterpret_cast(note_addr); ++ if (note_header.n_type != NT_GNU_BUILD_ID) continue; ++ const char* const note_contents = ++ reinterpret_cast(note_addr + sizeof(Elf32_Nhdr)); ++ // The note name contains the null terminator as well. ++ if (note_header.n_namesz != strlen(ELF_NOTE_GNU) + 1) continue; ++ if (strncmp(ELF_NOTE_GNU, note_contents, note_header.n_namesz) == 0) { ++ return {static_cast(note_header.n_descsz), ++ reinterpret_cast(note_contents + ++ note_header.n_namesz)}; ++ } ++ } ++ return {0, nullptr}; ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/os_thread.h b/runtime/vm/os_thread.h +index 0c768f6b23d..adb0aa62400 100644 +--- a/runtime/vm/os_thread.h ++++ b/runtime/vm/os_thread.h +@@ -21,6 +21,8 @@ + #include "vm/os_thread_fuchsia.h" + #elif defined(DART_HOST_OS_LINUX) + #include "vm/os_thread_linux.h" ++#elif defined(DART_HOST_OS_OHOS) ++#include "vm/os_thread_ohos.h" + #elif defined(DART_HOST_OS_MACOS) + #include "vm/os_thread_macos.h" + #elif defined(DART_HOST_OS_WINDOWS) +diff --git a/runtime/vm/os_thread_absl.cc b/runtime/vm/os_thread_absl.cc +index 8c4e7e2aecd..73cc2395238 100644 +--- a/runtime/vm/os_thread_absl.cc ++++ b/runtime/vm/os_thread_absl.cc +@@ -110,7 +110,7 @@ static void UnblockSIGPROF() { + // is used to ensure that the thread is properly destroyed if the thread just + // exits. + static void* ThreadStart(void* data_ptr) { +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + if (FLAG_worker_thread_priority != kMinInt) { + if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), + FLAG_worker_thread_priority) == -1) { +@@ -145,7 +145,7 @@ static void* ThreadStart(void* data_ptr) { + // pthread_setname_np ignores names that are too long rather than truncating. + char truncated_name[16]; + snprintf(truncated_name, ARRAY_SIZE(truncated_name), "%s", name); +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + pthread_setname_np(pthread_self(), truncated_name); + #elif defined(DART_HOST_OS_MACOS) + // Set the thread name. +@@ -224,7 +224,7 @@ ThreadId OSThread::GetCurrentThreadId() { + ThreadId OSThread::GetCurrentThreadTraceId() { + #if defined(DART_HOST_OS_ANDROID) + return GetCurrentThreadId(); +-#elif defined(DART_HOST_OS_LINUX) ++#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + return syscall(__NR_gettid); + #elif defined(DART_HOST_OS_MACOS) + return ThreadIdFromIntPtr(pthread_mach_thread_np(pthread_self())); +@@ -265,7 +265,7 @@ void OSThread::Join(ThreadJoinId id) { + + intptr_t OSThread::ThreadIdToIntPtr(ThreadId id) { + COMPILE_ASSERT(sizeof(id) <= sizeof(intptr_t)); +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + return static_cast(id); + #elif defined(DART_HOST_OS_MACOS) + return reinterpret_cast(id); +@@ -273,7 +273,7 @@ intptr_t OSThread::ThreadIdToIntPtr(ThreadId id) { + } + + ThreadId OSThread::ThreadIdFromIntPtr(intptr_t id) { +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + return static_cast(id); + #elif defined(DART_HOST_OS_MACOS) + return reinterpret_cast(id); +@@ -285,7 +285,7 @@ bool OSThread::Compare(ThreadId a, ThreadId b) { + } + + bool OSThread::GetCurrentStackBounds(uword* lower, uword* upper) { +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + pthread_attr_t attr; + // May fail on the main thread. + if (pthread_getattr_np(pthread_self(), &attr) != 0) { +diff --git a/runtime/vm/os_thread_ohos.cc b/runtime/vm/os_thread_ohos.cc +new file mode 100644 +index 00000000000..d5fbff8f79e +--- /dev/null ++++ b/runtime/vm/os_thread_ohos.cc +@@ -0,0 +1,508 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" // NOLINT ++ ++#if defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL) ++ ++#include "vm/os_thread.h" ++ ++#include // NOLINT ++#include ++#include ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "platform/address_sanitizer.h" ++#include "platform/assert.h" ++#include "platform/safe_stack.h" ++#include "platform/signal_blocker.h" ++#include "platform/utils.h" ++ ++#include "vm/flags.h" ++ ++namespace dart { ++ ++DEFINE_FLAG(int, ++ worker_thread_priority, ++ kMinInt, ++ "The thread priority the VM should use for new worker threads."); ++ ++#define VALIDATE_PTHREAD_RESULT(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ FATAL("pthread error: %d (%s)", result, \ ++ Utils::StrError(result, error_buf, kBufferSize)); \ ++ } ++ ++// Variation of VALIDATE_PTHREAD_RESULT for named objects. ++#if defined(PRODUCT) ++#define VALIDATE_PTHREAD_RESULT_NAMED(result) VALIDATE_PTHREAD_RESULT(result) ++#else ++#define VALIDATE_PTHREAD_RESULT_NAMED(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ FATAL("[%s] pthread error: %d (%s)", name_, result, \ ++ Utils::StrError(result, error_buf, kBufferSize)); \ ++ } ++#endif ++ ++#if defined(DEBUG) ++#define ASSERT_PTHREAD_SUCCESS(result) VALIDATE_PTHREAD_RESULT(result) ++#else ++// NOTE: This (currently) expands to a no-op. ++#define ASSERT_PTHREAD_SUCCESS(result) ASSERT(result == 0) ++#endif ++ ++#ifdef DEBUG ++#define RETURN_ON_PTHREAD_FAILURE(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ fprintf(stderr, "%s:%d: pthread error: %d (%s)\n", __FILE__, __LINE__, \ ++ result, Utils::StrError(result, error_buf, kBufferSize)); \ ++ return result; \ ++ } ++#else ++#define RETURN_ON_PTHREAD_FAILURE(result) \ ++ if (result != 0) return result; ++#endif ++ ++static void ComputeTimeSpecMicros(struct timespec* ts, int64_t micros) { ++ int64_t secs = micros / kMicrosecondsPerSecond; ++ int64_t nanos = ++ (micros - (secs * kMicrosecondsPerSecond)) * kNanosecondsPerMicrosecond; ++ int result = clock_gettime(CLOCK_MONOTONIC, ts); ++ ASSERT(result == 0); ++ ts->tv_sec += secs; ++ ts->tv_nsec += nanos; ++ if (ts->tv_nsec >= kNanosecondsPerSecond) { ++ ts->tv_sec += 1; ++ ts->tv_nsec -= kNanosecondsPerSecond; ++ } ++} ++ ++class ThreadStartData { ++ public: ++ ThreadStartData(const char* name, ++ OSThread::ThreadStartFunction function, ++ uword parameter) ++ : name_(name), function_(function), parameter_(parameter) {} ++ ++ const char* name() const { return name_; } ++ OSThread::ThreadStartFunction function() const { return function_; } ++ uword parameter() const { return parameter_; } ++ ++ private: ++ const char* name_; ++ OSThread::ThreadStartFunction function_; ++ uword parameter_; ++ ++ DISALLOW_COPY_AND_ASSIGN(ThreadStartData); ++}; ++ ++// TODO(bkonyi): remove this call once the prebuilt SDK is updated. ++// Spawned threads inherit their spawner's signal mask. We sometimes spawn ++// threads for running Dart code from a thread that is blocking SIGPROF. ++// This function explicitly unblocks SIGPROF so the profiler continues to ++// sample this thread. ++static void UnblockSIGPROF() { ++ sigset_t set; ++ sigemptyset(&set); ++ sigaddset(&set, SIGPROF); ++ int r = pthread_sigmask(SIG_UNBLOCK, &set, NULL); ++ USE(r); ++ ASSERT(r == 0); ++ ASSERT(!CHECK_IS_BLOCKING(SIGPROF)); ++} ++ ++// Dispatch to the thread start function provided by the caller. This trampoline ++// is used to ensure that the thread is properly destroyed if the thread just ++// exits. ++static void* ThreadStart(void* data_ptr) { ++ if (FLAG_worker_thread_priority != kMinInt) { ++ if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), ++ FLAG_worker_thread_priority) == -1) { ++ FATAL("Setting thread priority to %d failed: errno = %d\n", ++ FLAG_worker_thread_priority, errno); ++ } ++ } ++ ++ ThreadStartData* data = reinterpret_cast(data_ptr); ++ ++ const char* name = data->name(); ++ OSThread::ThreadStartFunction function = data->function(); ++ uword parameter = data->parameter(); ++ delete data; ++ ++ // Set the thread name. There is 16 bytes limit on the name (including \0). ++ // pthread_setname_np ignores names that are too long rather than truncating. ++ char truncated_name[16]; ++ snprintf(truncated_name, ARRAY_SIZE(truncated_name), "%s", name); ++ pthread_setname_np(pthread_self(), truncated_name); ++ ++ // Create new OSThread object and set as TLS for new thread. ++ OSThread* thread = OSThread::CreateOSThread(); ++ if (thread != NULL) { ++ OSThread::SetCurrent(thread); ++ thread->SetName(name); ++ UnblockSIGPROF(); ++ // Call the supplied thread start function handing it its parameters. ++ function(parameter); ++ } ++ ++ return NULL; ++} ++ ++int OSThread::Start(const char* name, ++ ThreadStartFunction function, ++ uword parameter) { ++ pthread_attr_t attr; ++ int result = pthread_attr_init(&attr); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_setstacksize(&attr, OSThread::GetMaxStackSize()); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ ThreadStartData* data = new ThreadStartData(name, function, parameter); ++ ++ pthread_t tid; ++ result = pthread_create(&tid, &attr, ThreadStart, data); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_destroy(&attr); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ return 0; ++} ++ ++const ThreadId OSThread::kInvalidThreadId = static_cast(0); ++const ThreadJoinId OSThread::kInvalidThreadJoinId = ++ static_cast(0); ++ ++ThreadLocalKey OSThread::CreateThreadLocal(ThreadDestructor destructor) { ++ pthread_key_t key = kUnsetThreadLocalKey; ++ int result = pthread_key_create(&key, destructor); ++ VALIDATE_PTHREAD_RESULT(result); ++ ASSERT(key != kUnsetThreadLocalKey); ++ return key; ++} ++ ++void OSThread::DeleteThreadLocal(ThreadLocalKey key) { ++ ASSERT(key != kUnsetThreadLocalKey); ++ int result = pthread_key_delete(key); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void OSThread::SetThreadLocal(ThreadLocalKey key, uword value) { ++ ASSERT(key != kUnsetThreadLocalKey); ++ int result = pthread_setspecific(key, reinterpret_cast(value)); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++intptr_t OSThread::GetMaxStackSize() { ++ const int kStackSize = (128 * kWordSize * KB); ++ return kStackSize; ++} ++ ++ThreadId OSThread::GetCurrentThreadId() { ++ return pthread_self(); ++} ++ ++#ifdef SUPPORT_TIMELINE ++ThreadId OSThread::GetCurrentThreadTraceId() { ++ return syscall(__NR_gettid); ++} ++#endif // SUPPORT_TIMELINE ++ ++char* OSThread::GetCurrentThreadName() { ++ const intptr_t kNameBufferSize = 16; ++ char* name = static_cast(malloc(kNameBufferSize)); ++ prctl(PR_GET_NAME, name); ++ return name; ++} ++ ++ThreadJoinId OSThread::GetCurrentThreadJoinId(OSThread* thread) { ++ ASSERT(thread != NULL); ++ // Make sure we're filling in the join id for the current thread. ++ ASSERT(thread->id() == GetCurrentThreadId()); ++ // Make sure the join_id_ hasn't been set, yet. ++ DEBUG_ASSERT(thread->join_id_ == kInvalidThreadJoinId); ++ pthread_t id = pthread_self(); ++#if defined(DEBUG) ++ thread->join_id_ = id; ++#endif ++ return id; ++} ++ ++void OSThread::Join(ThreadJoinId id) { ++ int result = pthread_join(id, NULL); ++ ASSERT(result == 0); ++} ++ ++intptr_t OSThread::ThreadIdToIntPtr(ThreadId id) { ++ ASSERT(sizeof(id) == sizeof(intptr_t)); ++ return static_cast(id); ++} ++ ++ThreadId OSThread::ThreadIdFromIntPtr(intptr_t id) { ++ return static_cast(id); ++} ++ ++bool OSThread::Compare(ThreadId a, ThreadId b) { ++ return pthread_equal(a, b) != 0; ++} ++ ++bool OSThread::GetCurrentStackBounds(uword* lower, uword* upper) { ++ pthread_attr_t attr; ++ // May fail on the main thread. ++ if (pthread_getattr_np(pthread_self(), &attr) != 0) { ++ return false; ++ } ++ ++ void* base; ++ size_t size; ++ int error = pthread_attr_getstack(&attr, &base, &size); ++ pthread_attr_destroy(&attr); ++ if (error != 0) { ++ return false; ++ } ++ ++ *lower = reinterpret_cast(base); ++ *upper = *lower + size; ++ return true; ++} ++ ++#if defined(USING_SAFE_STACK) ++NO_SANITIZE_ADDRESS ++NO_SANITIZE_SAFE_STACK ++uword OSThread::GetCurrentSafestackPointer() { ++#error "SAFE_STACK is unsupported on this platform" ++ return 0; ++} ++ ++NO_SANITIZE_ADDRESS ++NO_SANITIZE_SAFE_STACK ++void OSThread::SetCurrentSafestackPointer(uword ssp) { ++#error "SAFE_STACK is unsupported on this platform" ++} ++#endif ++ ++Mutex::Mutex(NOT_IN_PRODUCT(const char* name)) ++#if !defined(PRODUCT) ++ : name_(name) ++#endif ++{ ++ pthread_mutexattr_t attr; ++ int result = pthread_mutexattr_init(&attr); ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++ ++#if defined(DEBUG) ++ result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++#endif // defined(DEBUG) ++ ++ result = pthread_mutex_init(data_.mutex(), &attr); ++ // Verify that creating a pthread_mutex succeeded. ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++ ++ result = pthread_mutexattr_destroy(&attr); ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++} ++ ++Mutex::~Mutex() { ++ int result = pthread_mutex_destroy(data_.mutex()); ++ // Verify that the pthread_mutex was destroyed. ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++#endif // defined(DEBUG) ++} ++ ++void Mutex::Lock() { ++ int result = pthread_mutex_lock(data_.mutex()); ++ // Specifically check for dead lock to help debugging. ++ ASSERT(result != EDEADLK); ++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors. ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ owner_ = OSThread::GetCurrentThreadId(); ++#endif // defined(DEBUG) ++} ++ ++bool Mutex::TryLock() { ++ int result = pthread_mutex_trylock(data_.mutex()); ++ // Return false if the lock is busy and locking failed. ++ if (result == EBUSY) { ++ return false; ++ } ++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors. ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ owner_ = OSThread::GetCurrentThreadId(); ++#endif // defined(DEBUG) ++ return true; ++} ++ ++void Mutex::Unlock() { ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++ int result = pthread_mutex_unlock(data_.mutex()); ++ // Specifically check for wrong thread unlocking to aid debugging. ++ ASSERT(result != EPERM); ++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors. ++} ++ ++Monitor::Monitor() { ++ pthread_mutexattr_t mutex_attr; ++ int result = pthread_mutexattr_init(&mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ result = pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK); ++ VALIDATE_PTHREAD_RESULT(result); ++#endif // defined(DEBUG) ++ ++ result = pthread_mutex_init(data_.mutex(), &mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_mutexattr_destroy(&mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ pthread_condattr_t cond_attr; ++ result = pthread_condattr_init(&cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_cond_init(data_.cond(), &cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_condattr_destroy(&cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++} ++ ++Monitor::~Monitor() { ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++#endif // defined(DEBUG) ++ ++ int result = pthread_mutex_destroy(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_cond_destroy(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++bool Monitor::TryEnter() { ++ int result = pthread_mutex_trylock(data_.mutex()); ++ // Return false if the lock is busy and locking failed. ++ if (result == EBUSY) { ++ return false; ++ } ++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors. ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++ owner_ = OSThread::GetCurrentThreadId(); ++#endif // defined(DEBUG) ++ return true; ++} ++ ++void Monitor::Enter() { ++ int result = pthread_mutex_lock(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++ owner_ = OSThread::GetCurrentThreadId(); ++#endif // defined(DEBUG) ++} ++ ++void Monitor::Exit() { ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++ ++ int result = pthread_mutex_unlock(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++Monitor::WaitResult Monitor::Wait(int64_t millis) { ++ Monitor::WaitResult retval = WaitMicros(millis * kMicrosecondsPerMillisecond); ++ return retval; ++} ++ ++Monitor::WaitResult Monitor::WaitMicros(int64_t micros) { ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ ThreadId saved_owner = owner_; ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++ ++ Monitor::WaitResult retval = kNotified; ++ if (micros == kNoTimeout) { ++ // Wait forever. ++ int result = pthread_cond_wait(data_.cond(), data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ } else { ++ struct timespec ts; ++ ComputeTimeSpecMicros(&ts, micros); ++ int result = pthread_cond_timedwait(data_.cond(), data_.mutex(), &ts); ++ ASSERT((result == 0) || (result == ETIMEDOUT)); ++ if (result == ETIMEDOUT) { ++ retval = kTimedOut; ++ } ++ } ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++ owner_ = OSThread::GetCurrentThreadId(); ++ ASSERT(owner_ == saved_owner); ++#endif // defined(DEBUG) ++ return retval; ++} ++ ++void Monitor::Notify() { ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ int result = pthread_cond_signal(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void Monitor::NotifyAll() { ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ int result = pthread_cond_broadcast(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL) +diff --git a/runtime/vm/os_thread_ohos.h b/runtime/vm/os_thread_ohos.h +new file mode 100644 +index 00000000000..aaf490f9f77 +--- /dev/null ++++ b/runtime/vm/os_thread_ohos.h +@@ -0,0 +1,76 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#ifndef RUNTIME_VM_OS_THREAD_OHOS_H_ ++#define RUNTIME_VM_OS_THREAD_OHOS_H_ ++ ++#if !defined(RUNTIME_VM_OS_THREAD_H_) ++#error Do not include os_thread_linux.h directly; use os_thread.h instead. ++#endif ++ ++#include ++ ++#include "platform/assert.h" ++#include "platform/globals.h" ++ ++namespace dart { ++ ++typedef pthread_key_t ThreadLocalKey; ++typedef pthread_t ThreadId; ++typedef pthread_t ThreadJoinId; ++ ++static const ThreadLocalKey kUnsetThreadLocalKey = ++ static_cast(-1); ++ ++class ThreadInlineImpl { ++ private: ++ ThreadInlineImpl() {} ++ ~ThreadInlineImpl() {} ++ ++ static uword GetThreadLocal(ThreadLocalKey key) { ++ ASSERT(key != kUnsetThreadLocalKey); ++ return reinterpret_cast(pthread_getspecific(key)); ++ } ++ ++ friend class OSThread; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(ThreadInlineImpl); ++}; ++ ++class MutexData { ++ private: ++ MutexData() {} ++ ~MutexData() {} ++ ++ pthread_mutex_t* mutex() { return &mutex_; } ++ ++ pthread_mutex_t mutex_; ++ ++ friend class Mutex; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(MutexData); ++}; ++ ++class MonitorData { ++ private: ++ MonitorData() {} ++ ~MonitorData() {} ++ ++ pthread_mutex_t* mutex() { return &mutex_; } ++ pthread_cond_t* cond() { return &cond_; } ++ ++ pthread_mutex_t mutex_; ++ pthread_cond_t cond_; ++ ++ friend class Monitor; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(MonitorData); ++}; ++ ++} // namespace dart ++ ++#endif // RUNTIME_VM_OS_THREAD_OHOS_H_ +diff --git a/runtime/vm/proccpuinfo.cc b/runtime/vm/proccpuinfo.cc +index a6c1c250d1b..b955e6d7838 100644 +--- a/runtime/vm/proccpuinfo.cc ++++ b/runtime/vm/proccpuinfo.cc +@@ -3,7 +3,7 @@ + // BSD-style license that can be found in the LICENSE file. + + #include "vm/globals.h" +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + + #include "vm/proccpuinfo.h" + +@@ -146,4 +146,4 @@ bool ProcCpuInfo::HasField(const char* field) { + + } // namespace dart + +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/proccpuinfo.h b/runtime/vm/proccpuinfo.h +index ff249d42d2d..4c829f4ace6 100644 +--- a/runtime/vm/proccpuinfo.h ++++ b/runtime/vm/proccpuinfo.h +@@ -6,7 +6,7 @@ + #define RUNTIME_VM_PROCCPUINFO_H_ + + #include "vm/globals.h" +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + + #include "vm/allocation.h" + +@@ -29,6 +29,6 @@ class ProcCpuInfo : public AllStatic { + + } // namespace dart + +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + + #endif // RUNTIME_VM_PROCCPUINFO_H_ +diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc +index 6b64f4d4f5a..85a577b2c9e 100644 +--- a/runtime/vm/profiler.cc ++++ b/runtime/vm/profiler.cc +@@ -435,7 +435,7 @@ void Profiler::DumpStackTrace(void* context) { + return; + } + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + ucontext_t* ucontext = reinterpret_cast(context); + mcontext_t mcontext = ucontext->uc_mcontext; + uword pc = SignalHandler::GetProgramCounter(mcontext); +diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc +index 3c5593ea4f9..ebb9f661591 100644 +--- a/runtime/vm/service.cc ++++ b/runtime/vm/service.cc +@@ -4602,7 +4602,7 @@ static void RequestHeapSnapshot(Thread* thread, JSONStream* js) { + PrintSuccess(js); + } + +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + struct VMMapping { + char path[256]; + size_t size; +@@ -4810,7 +4810,7 @@ static intptr_t GetProcessMemoryUsageHelper(JSONStream* js) { + vm.AddProperty64("size", vm_size); + } + +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + AddVMMappings(&rss_children); + #endif + // TODO(46166): Implement for other operating systems. +diff --git a/runtime/vm/signal_handler.h b/runtime/vm/signal_handler.h +index bfcd982e915..fab0a1cea81 100644 +--- a/runtime/vm/signal_handler.h ++++ b/runtime/vm/signal_handler.h +@@ -33,6 +33,9 @@ typedef struct ucontext { + struct siginfo_t; + struct mcontext_t; + struct sigset_t {}; ++#elif defined(DART_HOST_OS_OHOS) ++#include // NOLINT ++#include // NOLINT + #elif defined(DART_HOST_OS_FUCHSIA) + #include // NOLINT + #include // NOLINT +diff --git a/runtime/vm/signal_handler_ohos.cc b/runtime/vm/signal_handler_ohos.cc +new file mode 100644 +index 00000000000..c64c9984606 +--- /dev/null ++++ b/runtime/vm/signal_handler_ohos.cc +@@ -0,0 +1,136 @@ ++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "vm/globals.h" ++#include "vm/instructions.h" ++#include "vm/signal_handler.h" ++#include "vm/simulator.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++namespace dart { ++ ++uintptr_t SignalHandler::GetProgramCounter(const mcontext_t& mcontext) { ++ uintptr_t pc = 0; ++ ++#if defined(HOST_ARCH_IA32) ++ pc = static_cast(mcontext.gregs[REG_EIP]); ++#elif defined(HOST_ARCH_X64) ++ pc = static_cast(mcontext.gregs[REG_RIP]); ++#elif defined(HOST_ARCH_ARM) ++ pc = static_cast(mcontext.arm_pc); ++#elif defined(HOST_ARCH_ARM64) ++ pc = static_cast(mcontext.pc); ++#elif defined(HOST_ARCH_RISCV32) ++ pc = static_cast(mcontext.__gregs[REG_PC]); ++#elif defined(HOST_ARCH_RISCV64) ++ pc = static_cast(mcontext.__gregs[REG_PC]); ++#else ++#error Unsupported architecture. ++#endif // HOST_ARCH_... ++ return pc; ++} ++ ++uintptr_t SignalHandler::GetFramePointer(const mcontext_t& mcontext) { ++ uintptr_t fp = 0; ++ ++#if defined(HOST_ARCH_IA32) ++ fp = static_cast(mcontext.gregs[REG_EBP]); ++#elif defined(HOST_ARCH_X64) ++ fp = static_cast(mcontext.gregs[REG_RBP]); ++#elif defined(HOST_ARCH_ARM) ++ // B1.3.3 Program Status Registers (PSRs) ++ if ((mcontext.arm_cpsr & (1 << 5)) != 0) { ++ // Thumb mode. ++ fp = static_cast(mcontext.arm_r7); ++ } else { ++ // ARM mode. ++ fp = static_cast(mcontext.arm_fp); ++ } ++#elif defined(HOST_ARCH_ARM64) ++ fp = static_cast(mcontext.regs[29]); ++#elif defined(HOST_ARCH_RISCV32) ++ fp = static_cast(mcontext.__gregs[REG_S0]); ++#elif defined(HOST_ARCH_RISCV64) ++ fp = static_cast(mcontext.__gregs[REG_S0]); ++#else ++#error Unsupported architecture. ++#endif // HOST_ARCH_... ++ ++ return fp; ++} ++ ++uintptr_t SignalHandler::GetCStackPointer(const mcontext_t& mcontext) { ++ uintptr_t sp = 0; ++ ++#if defined(HOST_ARCH_IA32) ++ sp = static_cast(mcontext.gregs[REG_ESP]); ++#elif defined(HOST_ARCH_X64) ++ sp = static_cast(mcontext.gregs[REG_RSP]); ++#elif defined(HOST_ARCH_ARM) ++ sp = static_cast(mcontext.arm_sp); ++#elif defined(HOST_ARCH_ARM64) ++ sp = static_cast(mcontext.sp); ++#elif defined(HOST_ARCH_RISCV32) ++ sp = static_cast(mcontext.__gregs[REG_SP]); ++#elif defined(HOST_ARCH_RISCV64) ++ sp = static_cast(mcontext.__gregs[REG_SP]); ++#else ++#error Unsupported architecture. ++#endif // HOST_ARCH_... ++ return sp; ++} ++ ++uintptr_t SignalHandler::GetDartStackPointer(const mcontext_t& mcontext) { ++#if defined(TARGET_ARCH_ARM64) && !defined(USING_SIMULATOR) ++ return static_cast(mcontext.regs[SPREG]); ++#else ++ return GetCStackPointer(mcontext); ++#endif ++} ++ ++uintptr_t SignalHandler::GetLinkRegister(const mcontext_t& mcontext) { ++ uintptr_t lr = 0; ++ ++#if defined(HOST_ARCH_IA32) ++ lr = 0; ++#elif defined(HOST_ARCH_X64) ++ lr = 0; ++#elif defined(HOST_ARCH_ARM) ++ lr = static_cast(mcontext.arm_lr); ++#elif defined(HOST_ARCH_ARM64) ++ lr = static_cast(mcontext.regs[30]); ++#elif defined(HOST_ARCH_RISCV32) ++ lr = static_cast(mcontext.__gregs[REG_RA]); ++#elif defined(HOST_ARCH_RISCV64) ++ lr = static_cast(mcontext.__gregs[REG_RA]); ++#else ++#error Unsupported architecture. ++#endif // HOST_ARCH_... ++ return lr; ++} ++ ++void SignalHandler::Install(SignalAction action) { ++ struct sigaction act = {}; ++ act.sa_handler = NULL; ++ act.sa_sigaction = action; ++ sigemptyset(&act.sa_mask); ++ act.sa_flags = SA_RESTART | SA_SIGINFO; ++ int r = sigaction(SIGPROF, &act, NULL); ++ ASSERT(r == 0); ++} ++ ++void SignalHandler::Remove() { ++ // Ignore future SIGPROF signals because by default SIGPROF will terminate ++ // the process and we may have some signals in flight. ++ struct sigaction act = {}; ++ act.sa_handler = SIG_IGN; ++ sigemptyset(&act.sa_mask); ++ act.sa_flags = 0; ++ int r = sigaction(SIGPROF, &act, NULL); ++ ASSERT(r == 0); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/thread_interrupter_ohos.cc b/runtime/vm/thread_interrupter_ohos.cc +new file mode 100644 +index 00000000000..67c4a6545ec +--- /dev/null ++++ b/runtime/vm/thread_interrupter_ohos.cc +@@ -0,0 +1,69 @@ ++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include // NOLINT ++ ++#include "vm/flags.h" ++#include "vm/os.h" ++#include "vm/profiler.h" ++#include "vm/signal_handler.h" ++#include "vm/thread_interrupter.h" ++ ++namespace dart { ++ ++#ifndef PRODUCT ++ ++DECLARE_FLAG(bool, trace_thread_interrupter); ++ ++class ThreadInterrupterOhos : public AllStatic { ++ public: ++ static void ThreadInterruptSignalHandler(int signal, ++ siginfo_t* info, ++ void* context_) { ++ if (signal != SIGPROF) { ++ return; ++ } ++ Thread* thread = Thread::Current(); ++ if (thread == NULL) { ++ return; ++ } ++ ThreadInterruptScope signal_handler_scope; ++ // Extract thread state. ++ ucontext_t* context = reinterpret_cast(context_); ++ mcontext_t mcontext = context->uc_mcontext; ++ InterruptedThreadState its; ++ its.pc = SignalHandler::GetProgramCounter(mcontext); ++ its.fp = SignalHandler::GetFramePointer(mcontext); ++ its.csp = SignalHandler::GetCStackPointer(mcontext); ++ its.dsp = SignalHandler::GetDartStackPointer(mcontext); ++ its.lr = SignalHandler::GetLinkRegister(mcontext); ++ Profiler::SampleThread(thread, its); ++ } ++}; ++ ++void ThreadInterrupter::InterruptThread(OSThread* thread) { ++ if (FLAG_trace_thread_interrupter) { ++ OS::PrintErr("ThreadInterrupter interrupting %p\n", ++ reinterpret_cast(thread->id())); ++ } ++ int result = pthread_kill(thread->id(), SIGPROF); ++ ASSERT((result == 0) || (result == ESRCH)); ++} ++ ++void ThreadInterrupter::InstallSignalHandler() { ++ SignalHandler::Install(&ThreadInterrupterOhos::ThreadInterruptSignalHandler); ++} ++ ++void ThreadInterrupter::RemoveSignalHandler() { ++ SignalHandler::Remove(); ++} ++ ++#endif // !PRODUCT ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc +index 02855e2622b..d3cc6a54118 100644 +--- a/runtime/vm/timeline.cc ++++ b/runtime/vm/timeline.cc +@@ -164,7 +164,7 @@ static TimelineEventRecorder* CreateTimelineRecorder() { + + // Systrace recorder. + if (strcmp("systrace", flag) == 0) { +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + return new TimelineEventSystraceRecorder(); + #elif defined(DART_HOST_OS_MACOS) + return new TimelineEventMacosRecorder(); +diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h +index 51329aa632c..340378d040a 100644 +--- a/runtime/vm/timeline.h ++++ b/runtime/vm/timeline.h +@@ -1215,7 +1215,7 @@ class TimelineEventFuchsiaRecorder : public TimelineEventPlatformRecorder { + }; + #endif // defined(DART_HOST_OS_FUCHSIA) + +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + // A recorder that writes events to Android Systrace. This class is exposed in + // this header file only so that PrintSystrace can be visible to + // timeline_test.cc. +@@ -1236,7 +1236,7 @@ class TimelineEventSystraceRecorder : public TimelineEventPlatformRecorder { + + int systrace_fd_; + }; +-#endif // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#endif // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + + #if defined(DART_HOST_OS_MACOS) + // A recorder that sends events to macOS's tracing app. See: +diff --git a/runtime/vm/timeline_ohos.cc b/runtime/vm/timeline_ohos.cc +new file mode 100644 +index 00000000000..27f2e225e34 +--- /dev/null ++++ b/runtime/vm/timeline_ohos.cc +@@ -0,0 +1,120 @@ ++// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "vm/globals.h" ++#if defined(DART_HOST_OS_OHOS) && defined(SUPPORT_TIMELINE) ++ ++#include ++#include ++#include ++ ++#include "platform/atomic.h" ++#include "platform/signal_blocker.h" ++#include "vm/isolate.h" ++#include "vm/json_stream.h" ++#include "vm/lockers.h" ++#include "vm/log.h" ++#include "vm/object.h" ++#include "vm/service_event.h" ++#include "vm/thread.h" ++#include "vm/timeline.h" ++ ++namespace dart { ++ ++DECLARE_FLAG(bool, trace_timeline); ++ ++static int OpenTraceFD() { ++ const char* kSystraceDebugPath = "/sys/kernel/debug/tracing/trace_marker"; ++ const char* kSystracePath = "/sys/kernel/tracing/trace_marker"; ++ ++ int fd = TEMP_FAILURE_RETRY(::open(kSystracePath, O_WRONLY)); ++ if (fd < 0) { ++ fd = TEMP_FAILURE_RETRY(::open(kSystraceDebugPath, O_WRONLY)); ++ } ++ ++ if (fd < 0 && FLAG_trace_timeline) { ++ OS::PrintErr("TimelineEventSystraceRecorder: Could not open `%s` or `%s`\n", ++ kSystraceDebugPath, kSystracePath); ++ } ++ return fd; ++} ++ ++TimelineEventSystraceRecorder::TimelineEventSystraceRecorder() ++ : TimelineEventPlatformRecorder(), systrace_fd_(OpenTraceFD()) { ++ Timeline::set_recorder_discards_clock_values(true); ++} ++ ++TimelineEventSystraceRecorder::~TimelineEventSystraceRecorder() { ++ if (systrace_fd_ >= 0) { ++ close(systrace_fd_); ++ } ++} ++ ++intptr_t TimelineEventSystraceRecorder::PrintSystrace(TimelineEvent* event, ++ char* buffer, ++ intptr_t buffer_size) { ++ ASSERT(buffer != NULL); ++ ASSERT(buffer_size > 0); ++ buffer[0] = '\0'; ++ intptr_t length = 0; ++ int64_t pid = OS::ProcessId(); ++ switch (event->event_type()) { ++ case TimelineEvent::kBegin: { ++ length = Utils::SNPrint(buffer, buffer_size, "B|%" Pd64 "|%s", pid, ++ event->label()); ++ break; ++ } ++ case TimelineEvent::kEnd: { ++ length = Utils::SNPrint(buffer, buffer_size, "E"); ++ break; ++ } ++ case TimelineEvent::kCounter: { ++ if (event->arguments_length() > 0) { ++ // We only report the first counter value. ++ length = Utils::SNPrint(buffer, buffer_size, "C|%" Pd64 "|%s|%s", pid, ++ event->label(), event->arguments()[0].value); ++ } ++ break; ++ } ++ case TimelineEvent::kAsyncBegin: { ++ length = Utils::SNPrint(buffer, buffer_size, "S|%" Pd64 "|%s|%" Pd64 "", ++ pid, event->label(), event->Id()); ++ break; ++ } ++ case TimelineEvent::kAsyncEnd: { ++ length = Utils::SNPrint(buffer, buffer_size, "F|%" Pd64 "|%s|%" Pd64 "", ++ pid, event->label(), event->Id()); ++ break; ++ } ++ default: ++ // Ignore event types that we cannot serialize to the Systrace format. ++ break; ++ } ++ return length; ++} ++ ++void TimelineEventSystraceRecorder::OnEvent(TimelineEvent* event) { ++ if (event == NULL) { ++ return; ++ } ++ if (systrace_fd_ < 0) { ++ return; ++ } ++ ++ // Serialize to the systrace format. ++ const intptr_t kBufferLength = 1024; ++ char buffer[kBufferLength]; ++ const intptr_t event_length = PrintSystrace(event, &buffer[0], kBufferLength); ++ if (event_length > 0) { ++ ssize_t result; ++ // Repeatedly attempt the write while we are being interrupted. ++ do { ++ result = write(systrace_fd_, buffer, event_length); ++ } while ((result == -1L) && (errno == EINTR)); ++ } ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) && !defined(PRODUCT) +diff --git a/runtime/vm/virtual_memory_posix.cc b/runtime/vm/virtual_memory_posix.cc +index 07d4dd537dc..a30f64bda56 100644 +--- a/runtime/vm/virtual_memory_posix.cc ++++ b/runtime/vm/virtual_memory_posix.cc +@@ -4,7 +4,7 @@ + + #include "vm/globals.h" + #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \ +- defined(DART_HOST_OS_MACOS) ++ defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + + #include "vm/virtual_memory.h" + +@@ -188,7 +188,7 @@ void VirtualMemory::Init() { + compressed_heap_->size()); + #endif // defined(DART_COMPRESSED_POINTERS) + +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + FILE* fp = fopen("/proc/sys/vm/max_map_count", "r"); + if (fp != nullptr) { + size_t max_map_count = 0; +@@ -276,6 +276,12 @@ VirtualMemory* VirtualMemory::AllocateAligned(intptr_t size, + } + #endif // defined(DART_HOST_OS_MACOS) + ++#if defined(DART_HOST_OS_OHOS) ++ if (is_executable) { ++ map_flags |= MAP_JIT_OHOS; ++ } ++#endif // defined(DART_HOST_OS_OHOS) ++ + void* hint = nullptr; + // Some 64-bit microarchitectures store only the low 32-bits of targets as + // part of indirect branch prediction, predicting that the target's upper bits +@@ -453,4 +459,4 @@ void VirtualMemory::DontNeed(void* address, intptr_t size) { + } // namespace dart + + #endif // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \ +- // defined(DART_HOST_OS_MACOS) ++ // defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni +index b893dcb7f68..17f8fc9829c 100644 +--- a/runtime/vm/vm_sources.gni ++++ b/runtime/vm/vm_sources.gni +@@ -69,6 +69,7 @@ vm_sources = [ + "cpuinfo_android.cc", + "cpuinfo_fuchsia.cc", + "cpuinfo_linux.cc", ++ "cpuinfo_ohos.cc", + "cpuinfo_macos.cc", + "cpuinfo_win.cc", + "dart.cc", +@@ -198,6 +199,7 @@ vm_sources = [ + "os_android.cc", + "os_fuchsia.cc", + "os_linux.cc", ++ "os_ohos.cc", + "os_macos.cc", + "os_thread.cc", + "os_thread.h", +@@ -213,6 +215,8 @@ vm_sources = [ + "os_thread_macos.h", + "os_thread_win.cc", + "os_thread_win.h", ++ "os_thread_ohos.cc", ++ "os_thread_ohos.h", + "os_win.cc", + "parser.cc", + "parser.h", +@@ -290,6 +294,7 @@ vm_sources = [ + "signal_handler_android.cc", + "signal_handler_fuchsia.cc", + "signal_handler_linux.cc", ++ "signal_handler_ohos.cc", + "signal_handler_macos.cc", + "signal_handler_win.cc", + "simulator.h", +@@ -329,6 +334,7 @@ vm_sources = [ + "thread_interrupter_android.cc", + "thread_interrupter_fuchsia.cc", + "thread_interrupter_linux.cc", ++ "thread_interrupter_ohos.cc", + "thread_interrupter_macos.cc", + "thread_interrupter_win.cc", + "thread_pool.cc", +@@ -344,6 +350,7 @@ vm_sources = [ + "timeline_android.cc", + "timeline_fuchsia.cc", + "timeline_linux.cc", ++ "timeline_ohos.cc", + "timeline_macos.cc", + "timer.cc", + "timer.h", +diff --git a/sdk/lib/_internal/vm/bin/vmservice_server.dart b/sdk/lib/_internal/vm/bin/vmservice_server.dart +index 46b1461b0a9..41c82382eca 100644 +--- a/sdk/lib/_internal/vm/bin/vmservice_server.dart ++++ b/sdk/lib/_internal/vm/bin/vmservice_server.dart +@@ -177,11 +177,11 @@ class Server { + this._service, + this._ip, + this._port, +- this._originCheckDisabled, ++ _originCheckDisabled, + bool authCodesDisabled, + this._serviceInfoFilename, + this._enableServicePortFallback) +- : _authCodesDisabled = (authCodesDisabled || Platform.isFuchsia); ++ : _authCodesDisabled = (authCodesDisabled || Platform.isFuchsia),this._originCheckDisabled=true; + + bool _isAllowedOrigin(String origin) { + Uri uri; +diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart +index d927f4fbd36..32648d41ec5 100644 +--- a/sdk/lib/_internal/vm/lib/ffi_patch.dart ++++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart +@@ -29,6 +29,8 @@ int get _intPtrSize => (const [ + 4, // androidIA32, + 8, // androidX64, + 8, // androidRiscv64, ++ 4, // ohosArm, ++ 8, // ohosArm64, + 8, // fuchsiaArm64, + 8, // fuchsiaX64, + 8, // fuchsiaRiscv64, +diff --git a/sdk/lib/ffi/abi.dart b/sdk/lib/ffi/abi.dart +index 8450a551d4a..3f72dae5e6e 100644 +--- a/sdk/lib/ffi/abi.dart ++++ b/sdk/lib/ffi/abi.dart +@@ -31,6 +31,10 @@ class Abi { + /// The application binary interface for Android on 64-bit RISC-V. + static const androidRiscv64 = _androidRiscv64; + ++ static const ohosArm = _ohosArm; ++ ++ static const ohosArm64 = _ohosArm64; ++ + /// The application binary interface for Fuchsia on the Arm64 architecture. + static const fuchsiaArm64 = _fuchsiaArm64; + +@@ -94,6 +98,8 @@ class Abi { + androidIA32, + androidX64, + androidRiscv64, ++ ohosArm, ++ ohosArm64, + fuchsiaArm64, + fuchsiaX64, + fuchsiaRiscv64, +@@ -138,6 +144,8 @@ class Abi { + static const _androidIA32 = Abi._(_Architecture.ia32, _OS.android); + static const _androidX64 = Abi._(_Architecture.x64, _OS.android); + static const _androidRiscv64 = Abi._(_Architecture.riscv64, _OS.android); ++ static const _ohosArm = Abi._(_Architecture.arm, _OS.ohos); ++ static const _ohosArm64 = Abi._(_Architecture.arm64, _OS.ohos); + static const _fuchsiaArm64 = Abi._(_Architecture.arm64, _OS.fuchsia); + static const _fuchsiaX64 = Abi._(_Architecture.x64, _OS.fuchsia); + static const _fuchsiaRiscv64 = Abi._(_Architecture.riscv64, _OS.fuchsia); +@@ -170,6 +178,7 @@ enum _Architecture { + /// The operating systems the Dart VM runs on. + enum _OS { + android, ++ ohos, + fuchsia, + ios, + linux, +diff --git a/sdk/lib/ffi/abi_specific.dart b/sdk/lib/ffi/abi_specific.dart +index 22e12b6c50b..c23d920dca2 100644 +--- a/sdk/lib/ffi/abi_specific.dart ++++ b/sdk/lib/ffi/abi_specific.dart +@@ -25,6 +25,8 @@ part of dart.ffi; + /// Abi.androidIA32: Uint32(), + /// Abi.androidX64: Uint64(), + /// Abi.androidRiscv64: Uint64(), ++/// Abi.ohosArm: Uint32(), ++/// Abi.ohosArm64: Uint64(), + /// Abi.fuchsiaArm64: Uint64(), + /// Abi.fuchsiaX64: Uint64(), + /// Abi.fuchsiaRiscv64: Uint64(), +diff --git a/sdk/lib/ffi/c_type.dart b/sdk/lib/ffi/c_type.dart +index dc31bc985c7..9de9faead72 100644 +--- a/sdk/lib/ffi/c_type.dart ++++ b/sdk/lib/ffi/c_type.dart +@@ -28,6 +28,8 @@ part of dart.ffi; + Abi.androidIA32: Int8(), + Abi.androidX64: Int8(), + Abi.androidRiscv64: Uint8(), ++ Abi.ohosArm: Uint8(), ++ Abi.ohosArm64: Uint8(), + Abi.fuchsiaArm64: Uint8(), + Abi.fuchsiaX64: Int8(), + Abi.fuchsiaRiscv64: Uint8(), +@@ -67,6 +69,8 @@ final class Char extends AbiSpecificInteger { + Abi.androidIA32: Int8(), + Abi.androidX64: Int8(), + Abi.androidRiscv64: Int8(), ++ Abi.ohosArm: Uint8(), ++ Abi.ohosArm64: Uint8(), + Abi.fuchsiaArm64: Int8(), + Abi.fuchsiaX64: Int8(), + Abi.fuchsiaRiscv64: Int8(), +@@ -106,6 +110,8 @@ final class SignedChar extends AbiSpecificInteger { + Abi.androidIA32: Uint8(), + Abi.androidX64: Uint8(), + Abi.androidRiscv64: Uint8(), ++ Abi.ohosArm: Uint8(), ++ Abi.ohosArm64: Uint8(), + Abi.fuchsiaArm64: Uint8(), + Abi.fuchsiaX64: Uint8(), + Abi.fuchsiaRiscv64: Uint8(), +@@ -145,6 +151,8 @@ final class UnsignedChar extends AbiSpecificInteger { + Abi.androidIA32: Int16(), + Abi.androidX64: Int16(), + Abi.androidRiscv64: Int16(), ++ Abi.ohosArm: Int16(), ++ Abi.ohosArm64: Int16(), + Abi.fuchsiaArm64: Int16(), + Abi.fuchsiaX64: Int16(), + Abi.fuchsiaRiscv64: Int16(), +@@ -184,6 +192,8 @@ final class Short extends AbiSpecificInteger { + Abi.androidIA32: Uint16(), + Abi.androidX64: Uint16(), + Abi.androidRiscv64: Uint16(), ++ Abi.ohosArm: Int16(), ++ Abi.ohosArm64: Int16(), + Abi.fuchsiaArm64: Uint16(), + Abi.fuchsiaX64: Uint16(), + Abi.fuchsiaRiscv64: Uint16(), +@@ -223,6 +233,8 @@ final class UnsignedShort extends AbiSpecificInteger { + Abi.androidIA32: Int32(), + Abi.androidX64: Int32(), + Abi.androidRiscv64: Int32(), ++ Abi.ohosArm: Int32(), ++ Abi.ohosArm64: Int32(), + Abi.fuchsiaArm64: Int32(), + Abi.fuchsiaX64: Int32(), + Abi.fuchsiaRiscv64: Int32(), +@@ -262,6 +274,8 @@ final class Int extends AbiSpecificInteger { + Abi.androidIA32: Uint32(), + Abi.androidX64: Uint32(), + Abi.androidRiscv64: Uint32(), ++ Abi.ohosArm: Uint32(), ++ Abi.ohosArm64: Uint32(), + Abi.fuchsiaArm64: Uint32(), + Abi.fuchsiaX64: Uint32(), + Abi.fuchsiaRiscv64: Uint32(), +@@ -302,6 +316,8 @@ final class UnsignedInt extends AbiSpecificInteger { + Abi.androidIA32: Int32(), + Abi.androidX64: Int64(), + Abi.androidRiscv64: Int64(), ++ Abi.ohosArm: Int32(), ++ Abi.ohosArm64: Int64(), + Abi.fuchsiaArm64: Int64(), + Abi.fuchsiaX64: Int64(), + Abi.fuchsiaRiscv64: Int64(), +@@ -342,6 +358,8 @@ final class Long extends AbiSpecificInteger { + Abi.androidIA32: Uint32(), + Abi.androidX64: Uint64(), + Abi.androidRiscv64: Uint64(), ++ Abi.ohosArm: Uint32(), ++ Abi.ohosArm64: Uint64(), + Abi.fuchsiaArm64: Uint64(), + Abi.fuchsiaX64: Uint64(), + Abi.fuchsiaRiscv64: Uint64(), +@@ -381,6 +399,8 @@ final class UnsignedLong extends AbiSpecificInteger { + Abi.androidIA32: Int64(), + Abi.androidX64: Int64(), + Abi.androidRiscv64: Int64(), ++ Abi.ohosArm: Int64(), ++ Abi.ohosArm64: Int64(), + Abi.fuchsiaArm64: Int64(), + Abi.fuchsiaX64: Int64(), + Abi.fuchsiaRiscv64: Int64(), +@@ -420,6 +440,8 @@ final class LongLong extends AbiSpecificInteger { + Abi.androidIA32: Uint64(), + Abi.androidX64: Uint64(), + Abi.androidRiscv64: Uint64(), ++ Abi.ohosArm: Uint64(), ++ Abi.ohosArm64: Uint64(), + Abi.fuchsiaArm64: Uint64(), + Abi.fuchsiaX64: Uint64(), + Abi.fuchsiaRiscv64: Uint64(), +@@ -454,6 +476,8 @@ final class UnsignedLongLong extends AbiSpecificInteger { + Abi.androidIA32: Int32(), + Abi.androidX64: Int64(), + Abi.androidRiscv64: Int64(), ++ Abi.ohosArm: Int32(), ++ Abi.ohosArm64: Int64(), + Abi.fuchsiaArm64: Int64(), + Abi.fuchsiaX64: Int64(), + Abi.fuchsiaRiscv64: Int64(), +@@ -489,6 +513,8 @@ final class IntPtr extends AbiSpecificInteger { + Abi.androidIA32: Uint32(), + Abi.androidX64: Uint64(), + Abi.androidRiscv64: Uint64(), ++ Abi.ohosArm: Uint32(), ++ Abi.ohosArm64: Uint64(), + Abi.fuchsiaArm64: Uint64(), + Abi.fuchsiaX64: Uint64(), + Abi.fuchsiaRiscv64: Uint64(), +@@ -524,6 +550,8 @@ final class UintPtr extends AbiSpecificInteger { + Abi.androidIA32: Uint32(), + Abi.androidX64: Uint64(), + Abi.androidRiscv64: Uint64(), ++ Abi.ohosArm: Uint32(), ++ Abi.ohosArm64: Uint64(), + Abi.fuchsiaArm64: Uint64(), + Abi.fuchsiaX64: Uint64(), + Abi.fuchsiaRiscv64: Uint64(), +@@ -562,6 +590,8 @@ final class Size extends AbiSpecificInteger { + Abi.androidIA32: Uint32(), + Abi.androidX64: Uint32(), + Abi.androidRiscv64: Int32(), ++ Abi.ohosArm: Uint32(), ++ Abi.ohosArm64: Uint32(), + Abi.fuchsiaArm64: Uint32(), + Abi.fuchsiaX64: Int32(), + Abi.fuchsiaRiscv64: Int32(), +diff --git a/sdk/lib/io/platform.dart b/sdk/lib/io/platform.dart +index fdb965034f1..14d9607620a 100644 +--- a/sdk/lib/io/platform.dart ++++ b/sdk/lib/io/platform.dart +@@ -165,6 +165,9 @@ final class Platform { + @pragma("vm:platform-const") + static final bool isFuchsia = (operatingSystem == "fuchsia"); + ++ /// Whether the operating system is a version of Ohos ++ static final bool isOhos = (operatingSystem == "ohos"); ++ + /// The environment for this process as a map from string key to string value. + /// + /// The map is unmodifiable, +diff --git a/sdk_args.gni b/sdk_args.gni +index b511f76d3e9..18c8c7e8bda 100644 +--- a/sdk_args.gni ++++ b/sdk_args.gni +@@ -23,7 +23,8 @@ declare_args() { + + # Whether to enable the SDK hash check that will prevent loading a kernel + # into a VM which was built with a different SDK. +- verify_sdk_hash = true ++ #verify_sdk_hash = true ++ verify_sdk_hash = false + + # When verify_sdk_hash is true, this string is used as the verification hash + # instead of calculating one from the contents of the tree using the + diff --git a/attachment/repos/dart.debug_tmp.patch b/attachment/repos/dart.debug_tmp.patch new file mode 100644 index 0000000000000000000000000000000000000000..6e7b3fa0357a9e6f8270692eb228e1c806ba543d --- /dev/null +++ b/attachment/repos/dart.debug_tmp.patch @@ -0,0 +1,251 @@ +diff --git a/runtime/bin/file_ohos.cc b/runtime/bin/file_ohos.cc +index 8ac60ad5e64..27066ae440b 100644 +--- a/runtime/bin/file_ohos.cc ++++ b/runtime/bin/file_ohos.cc +@@ -16,6 +16,7 @@ + #include // NOLINT + #include // NOLINT + #include // NOLINT ++#include + + #include "bin/builtin.h" + #include "bin/fdutils.h" +@@ -92,7 +93,7 @@ MappedMemory* File::Map(MapType type, + // Try to allocate near the VM's binary. + hint = reinterpret_cast(&Dart_Initialize); + prot = PROT_READ | PROT_EXEC; +- flags |= (MAP_JIT_OHOS | MAP_ANONYMOUS); ++ flags |= MAP_ANONYMOUS; + break; + case kReadWrite: + prot = PROT_READ | PROT_WRITE; +@@ -102,7 +103,17 @@ MappedMemory* File::Map(MapType type, + hint = start; + flags |= MAP_FIXED; + } ++ ++#if defined(DEBUG) ++ prctl(PRCTL_SET_JITFORT, 0, 0); ++#endif // defined(DEBUG) ++ + void* addr = mmap(hint, length, prot, flags, handle_->fd(), position); ++ ++#if defined(DEBUG) ++ prctl(PRCTL_SET_JITFORT, 0, 1); ++#endif // defined(DEBUG) ++ + if (addr == MAP_FAILED) { + return NULL; + } +diff --git a/runtime/bin/virtual_memory_posix.cc b/runtime/bin/virtual_memory_posix.cc +index 5a86a3cd5d1..298fa98c622 100644 +--- a/runtime/bin/virtual_memory_posix.cc ++++ b/runtime/bin/virtual_memory_posix.cc +@@ -12,7 +12,7 @@ + #include + #include + +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + #include + #endif + +@@ -50,19 +50,21 @@ VirtualMemory* VirtualMemory::Allocate(intptr_t size, + } + #endif // defined(DART_HOST_OS_MACOS) + +-#if defined(DART_HOST_OS_OHOS) +- if (is_executable) { +- map_flags |= MAP_JIT_OHOS; +- } +-#endif // defined(DART_HOST_OS_OHOS) +- + // Some 64-bit microarchitectures store only the low 32-bits of targets as + // part of indirect branch prediction, predicting that the target's upper bits + // will be same as the call instruction's address. This leads to misprediction + // for indirect calls crossing a 4GB boundary. We ask mmap to place our + // generated code near the VM binary to avoid this. + void* hint = is_executable ? reinterpret_cast(&Allocate) : nullptr; ++ ++#if defined(DART_HOST_OS_OHOS) ++ prctl(PRCTL_SET_JITFORT, 0, 0); ++#endif // defined(DART_HOST_OS_OHOS) + void* address = mmap(hint, size, prot, map_flags, -1, 0); ++#if defined(DART_HOST_OS_OHOS) ++ prctl(PRCTL_SET_JITFORT, 0, 1); ++#endif // defined(DART_HOST_OS_OHOS) ++ + #if defined(DART_HOST_OS_LINUX) + // On WSL 1 trying to allocate memory close to the binary by supplying a hint + // fails with ENOMEM for unclear reason. Some reports suggest that this might +@@ -127,6 +129,10 @@ void VirtualMemory::Protect(void* address, intptr_t size, Protection mode) { + prot = PROT_READ | PROT_WRITE | PROT_EXEC; + break; + } ++ ++#if defined(DART_HOST_OS_OHOS) ++ prctl(PRCTL_SET_JITFORT, 0, 0); ++#endif // defined(DART_HOST_OS_OHOS) + if (mprotect(reinterpret_cast(page_address), + end_address - page_address, prot) != 0) { + int error = errno; +@@ -135,6 +141,9 @@ void VirtualMemory::Protect(void* address, intptr_t size, Protection mode) { + FATAL("mprotect error: %d (%s)", error, + Utils::StrError(error, error_buf, kBufferSize)); + } ++#if defined(DART_HOST_OS_OHOS) ++ prctl(PRCTL_SET_JITFORT, 0, 1); ++#endif // defined(DART_HOST_OS_OHOS) + } + + } // namespace bin +diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h +index 5c1d340af81..83e93ce5bf2 100644 +--- a/runtime/platform/globals.h ++++ b/runtime/platform/globals.h +@@ -112,7 +112,7 @@ + #elif defined(DART_TARGET_OS_OHOS) && defined(DART_RUNTIME_OS_OHOS) + + #define DART_HOST_OS_OHOS 1 +-#define MAP_JIT_OHOS 0x1000 ++#define PRCTL_SET_JITFORT 0x6a6974 + + #elif defined(__linux__) || defined(__FreeBSD__) + +diff --git a/runtime/vm/os_ohos.cc b/runtime/vm/os_ohos.cc +index e28cf5f1b9b..86449ecb952 100644 +--- a/runtime/vm/os_ohos.cc ++++ b/runtime/vm/os_ohos.cc +@@ -21,6 +21,7 @@ + #include // NOLINT + #include // NOLINT + #include // NOLINT ++#include + + #include "platform/memory_sanitizer.h" + #include "platform/utils.h" +@@ -160,8 +161,14 @@ class JitDumpCodeObserver : public CodeObserver { + return; + } + ++#if defined(DEBUG) ++ prctl(PRCTL_SET_JITFORT, 0, 0); ++#endif // defined(DEBUG) + mapped_ = + mmap(nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0); ++#if defined(DEBUG) ++ prctl(PRCTL_SET_JITFORT, 0, 1); ++#endif // defined(DEBUG) + if (mapped_ == nullptr) { + close(fd); + return; +diff --git a/runtime/vm/virtual_memory_posix.cc b/runtime/vm/virtual_memory_posix.cc +index a30f64bda56..b6b9d9a49a1 100644 +--- a/runtime/vm/virtual_memory_posix.cc ++++ b/runtime/vm/virtual_memory_posix.cc +@@ -7,6 +7,7 @@ + defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + + #include "vm/virtual_memory.h" ++#include "vm/os_thread.h" + + #include + #include +@@ -15,7 +16,7 @@ + #include + #include + +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + #include + #endif + +@@ -50,6 +51,10 @@ DECLARE_FLAG(bool, generate_perf_events_symbols); + DECLARE_FLAG(bool, generate_perf_jitdump); + #endif + ++#if defined(DART_HOST_OS_OHOS) ++ static Mutex prctl_lock_; ++#endif ++ + uword VirtualMemory::page_size_ = 0; + VirtualMemory* VirtualMemory::compressed_heap_ = nullptr; + +@@ -276,12 +281,6 @@ VirtualMemory* VirtualMemory::AllocateAligned(intptr_t size, + } + #endif // defined(DART_HOST_OS_MACOS) + +-#if defined(DART_HOST_OS_OHOS) +- if (is_executable) { +- map_flags |= MAP_JIT_OHOS; +- } +-#endif // defined(DART_HOST_OS_OHOS) +- + void* hint = nullptr; + // Some 64-bit microarchitectures store only the low 32-bits of targets as + // part of indirect branch prediction, predicting that the target's upper bits +@@ -291,8 +290,19 @@ VirtualMemory* VirtualMemory::AllocateAligned(intptr_t size, + if (is_executable) { + hint = reinterpret_cast(&Dart_Initialize); + } ++ ++#if defined(DART_HOST_OS_OHOS) ++ MutexLocker ml(&prctl_lock_); ++ prctl(PRCTL_SET_JITFORT, 0, 0); ++#endif // defined(DART_HOST_OS_OHOS) ++ + void* address = + GenericMapAligned(hint, prot, size, alignment, allocated_size, map_flags); ++ ++#if defined(DART_HOST_OS_OHOS) ++ prctl(PRCTL_SET_JITFORT, 0, 1); ++#endif // defined(DART_HOST_OS_OHOS) ++ + #if defined(DART_HOST_OS_LINUX) + // On WSL 1 trying to allocate memory close to the binary by supplying a hint + // fails with ENOMEM for unclear reason. Some reports suggest that this might +@@ -301,8 +311,17 @@ VirtualMemory* VirtualMemory::AllocateAligned(intptr_t size, + // hint. + if (address == nullptr && hint != nullptr && + Utils::IsWindowsSubsystemForLinux()) { ++#if defined(DART_HOST_OS_OHOS) ++ MutexLocker ml(&prctl_lock_); ++ prctl(PRCTL_SET_JITFORT, 0, 0); ++#endif // defined(DART_HOST_OS_OHOS) ++ + address = GenericMapAligned(nullptr, prot, size, alignment, allocated_size, + map_flags); ++ ++#if defined(DART_HOST_OS_OHOS) ++ prctl(PRCTL_SET_JITFORT, 0, 1); ++#endif // defined(DART_HOST_OS_OHOS) + } + #endif + if (address == nullptr) { +@@ -423,6 +442,12 @@ void VirtualMemory::Protect(void* address, intptr_t size, Protection mode) { + prot = PROT_READ | PROT_WRITE | PROT_EXEC; + break; + } ++ ++#if defined(DART_HOST_OS_OHOS) ++ MutexLocker ml(&prctl_lock_); ++ prctl(PRCTL_SET_JITFORT, 0, 0); ++#endif // defined(DART_HOST_OS_OHOS) ++ + if (mprotect(reinterpret_cast(page_address), + end_address - page_address, prot) != 0) { + int error = errno; +@@ -433,6 +458,11 @@ void VirtualMemory::Protect(void* address, intptr_t size, Protection mode) { + FATAL("mprotect failed: %d (%s)", error, + Utils::StrError(error, error_buf, kBufferSize)); + } ++ ++#if defined(DART_HOST_OS_OHOS) ++ prctl(PRCTL_SET_JITFORT, 0, 1); ++#endif // defined(DART_HOST_OS_OHOS) ++ + LOG_INFO("mprotect(0x%" Px ", 0x%" Px ", %u) ok\n", page_address, + end_address - page_address, prot); + } +-- + diff --git a/attachment/repos/dart.debug_tmp1.patch b/attachment/repos/dart.debug_tmp1.patch new file mode 100644 index 0000000000000000000000000000000000000000..e07c59775240cb119ca65fb6b9d399572ea27d39 --- /dev/null +++ b/attachment/repos/dart.debug_tmp1.patch @@ -0,0 +1,157 @@ +diff --git a/runtime/bin/file_ohos.cc b/runtime/bin/file_ohos.cc +index 27066ae440b..ce636b02a12 100644 +--- a/runtime/bin/file_ohos.cc ++++ b/runtime/bin/file_ohos.cc +@@ -104,15 +104,19 @@ MappedMemory* File::Map(MapType type, + flags |= MAP_FIXED; + } + +-#if defined(DEBUG) ++#if defined(DART_HOST_OS_OHOS) ++ if (prot & PROT_EXEC) { + prctl(PRCTL_SET_JITFORT, 0, 0); +-#endif // defined(DEBUG) ++ } ++#endif // defined(DART_HOST_OS_OHOS) + + void* addr = mmap(hint, length, prot, flags, handle_->fd(), position); + +-#if defined(DEBUG) ++#if defined(DART_HOST_OS_OHOS) ++ if (prot & PROT_EXEC) { + prctl(PRCTL_SET_JITFORT, 0, 1); +-#endif // defined(DEBUG) ++ } ++#endif // defined(DART_HOST_OS_OHOS) + + if (addr == MAP_FAILED) { + return NULL; +diff --git a/runtime/bin/virtual_memory_posix.cc b/runtime/bin/virtual_memory_posix.cc +index 298fa98c622..21f25edf4e5 100644 +--- a/runtime/bin/virtual_memory_posix.cc ++++ b/runtime/bin/virtual_memory_posix.cc +@@ -58,11 +58,15 @@ VirtualMemory* VirtualMemory::Allocate(intptr_t size, + void* hint = is_executable ? reinterpret_cast(&Allocate) : nullptr; + + #if defined(DART_HOST_OS_OHOS) ++ if (prot & PROT_EXEC) { + prctl(PRCTL_SET_JITFORT, 0, 0); ++ } + #endif // defined(DART_HOST_OS_OHOS) + void* address = mmap(hint, size, prot, map_flags, -1, 0); + #if defined(DART_HOST_OS_OHOS) ++ if (prot & PROT_EXEC) { + prctl(PRCTL_SET_JITFORT, 0, 1); ++ } + #endif // defined(DART_HOST_OS_OHOS) + + #if defined(DART_HOST_OS_LINUX) +@@ -131,7 +135,9 @@ void VirtualMemory::Protect(void* address, intptr_t size, Protection mode) { + } + + #if defined(DART_HOST_OS_OHOS) ++ if (prot & PROT_EXEC) { + prctl(PRCTL_SET_JITFORT, 0, 0); ++ } + #endif // defined(DART_HOST_OS_OHOS) + if (mprotect(reinterpret_cast(page_address), + end_address - page_address, prot) != 0) { +@@ -142,7 +148,9 @@ void VirtualMemory::Protect(void* address, intptr_t size, Protection mode) { + Utils::StrError(error, error_buf, kBufferSize)); + } + #if defined(DART_HOST_OS_OHOS) ++ if (prot & PROT_EXEC) { + prctl(PRCTL_SET_JITFORT, 0, 1); ++ } + #endif // defined(DART_HOST_OS_OHOS) + } + +diff --git a/runtime/vm/os_ohos.cc b/runtime/vm/os_ohos.cc +index 86449ecb952..ec67b1dfc1c 100644 +--- a/runtime/vm/os_ohos.cc ++++ b/runtime/vm/os_ohos.cc +@@ -161,14 +161,14 @@ class JitDumpCodeObserver : public CodeObserver { + return; + } + +-#if defined(DEBUG) ++#if defined(DART_HOST_OS_OHOS) + prctl(PRCTL_SET_JITFORT, 0, 0); +-#endif // defined(DEBUG) ++#endif // defined(DART_HOST_OS_OHOS) + mapped_ = + mmap(nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0); +-#if defined(DEBUG) ++#if defined(DART_HOST_OS_OHOS) + prctl(PRCTL_SET_JITFORT, 0, 1); +-#endif // defined(DEBUG) ++#endif // defined(DART_HOST_OS_OHOS) + if (mapped_ == nullptr) { + close(fd); + return; +diff --git a/runtime/vm/virtual_memory_posix.cc b/runtime/vm/virtual_memory_posix.cc +index b6b9d9a49a1..a39987342b9 100644 +--- a/runtime/vm/virtual_memory_posix.cc ++++ b/runtime/vm/virtual_memory_posix.cc +@@ -292,15 +292,19 @@ VirtualMemory* VirtualMemory::AllocateAligned(intptr_t size, + } + + #if defined(DART_HOST_OS_OHOS) ++ if (prot & PROT_EXEC) { + MutexLocker ml(&prctl_lock_); + prctl(PRCTL_SET_JITFORT, 0, 0); ++ } + #endif // defined(DART_HOST_OS_OHOS) + + void* address = + GenericMapAligned(hint, prot, size, alignment, allocated_size, map_flags); + + #if defined(DART_HOST_OS_OHOS) ++ if (prot & PROT_EXEC) { + prctl(PRCTL_SET_JITFORT, 0, 1); ++ } + #endif // defined(DART_HOST_OS_OHOS) + + #if defined(DART_HOST_OS_LINUX) +@@ -312,15 +316,19 @@ VirtualMemory* VirtualMemory::AllocateAligned(intptr_t size, + if (address == nullptr && hint != nullptr && + Utils::IsWindowsSubsystemForLinux()) { + #if defined(DART_HOST_OS_OHOS) ++ if (prot & PROT_EXEC) { + MutexLocker ml(&prctl_lock_); + prctl(PRCTL_SET_JITFORT, 0, 0); ++ } + #endif // defined(DART_HOST_OS_OHOS) + + address = GenericMapAligned(nullptr, prot, size, alignment, allocated_size, + map_flags); + + #if defined(DART_HOST_OS_OHOS) ++ if (prot & PROT_EXEC) { + prctl(PRCTL_SET_JITFORT, 0, 1); ++ } + #endif // defined(DART_HOST_OS_OHOS) + } + #endif +@@ -444,8 +452,10 @@ void VirtualMemory::Protect(void* address, intptr_t size, Protection mode) { + } + + #if defined(DART_HOST_OS_OHOS) ++ if (prot & PROT_EXEC) { + MutexLocker ml(&prctl_lock_); + prctl(PRCTL_SET_JITFORT, 0, 0); ++ } + #endif // defined(DART_HOST_OS_OHOS) + + if (mprotect(reinterpret_cast(page_address), +@@ -460,7 +470,9 @@ void VirtualMemory::Protect(void* address, intptr_t size, Protection mode) { + } + + #if defined(DART_HOST_OS_OHOS) ++ if (prot & PROT_EXEC) { + prctl(PRCTL_SET_JITFORT, 0, 1); ++ } + #endif // defined(DART_HOST_OS_OHOS) + + LOG_INFO("mprotect(0x%" Px ", 0x%" Px ", %u) ok\n", page_address, +-- diff --git a/attachment/repos/dart.patch0 b/attachment/repos/dart.patch0 new file mode 100644 index 0000000000000000000000000000000000000000..04ca043cec6ffd71361c290ec56a2728f68b4704 --- /dev/null +++ b/attachment/repos/dart.patch0 @@ -0,0 +1,7635 @@ +diff --git a/pkg/dds/lib/dds.dart b/pkg/dds/lib/dds.dart +index 177a9265649..c7038c56ad7 100644 +--- a/pkg/dds/lib/dds.dart ++++ b/pkg/dds/lib/dds.dart +@@ -45,7 +45,7 @@ abstract class DartDevelopmentService { + static Future startDartDevelopmentService( + Uri remoteVmServiceUri, { + Uri? serviceUri, +- bool enableAuthCodes = true, ++ bool enableAuthCodes = false, + bool ipv6 = false, + bool enableServicePortFallback = false, + List cachedUserTags = const [], +diff --git a/pkg/dds/lib/src/dap/adapters/dart.dart b/pkg/dds/lib/src/dap/adapters/dart.dart +index 7cc20b487ed..552048e2e24 100644 +--- a/pkg/dds/lib/src/dap/adapters/dart.dart ++++ b/pkg/dds/lib/src/dap/adapters/dart.dart +@@ -450,7 +450,7 @@ abstract class DartDebugAdapter> _output, { + this.ipv6 = false, + this.enableDds = true, +- this.enableAuthCodes = true, ++ this.enableAuthCodes = false, + this.test = false, + this.logger, + Function? onError, +diff --git a/pkg/dds/lib/src/dds_impl.dart b/pkg/dds/lib/src/dds_impl.dart +index 33be98fc0aa..eda146a0580 100644 +--- a/pkg/dds/lib/src/dds_impl.dart ++++ b/pkg/dds/lib/src/dds_impl.dart +@@ -67,7 +67,10 @@ class DartDevelopmentServiceImpl implements DartDevelopmentService { + _isolateManager = IsolateManager(this); + _streamManager = StreamManager(this); + _packageUriConverter = PackageUriConverter(this); +- _authCode = _authCodesEnabled ? _makeAuthToken() : ''; ++ _authCode = _authCodesEnabled ? _makeAuthToken() : ''; ++ //TODO 临时关闭 ++ this._authCodesEnabled = false; ++ _authCode = "";//_authCodesEnabled ? _makeAuthToken() : ''; + } + + Future startService() async { +@@ -395,7 +398,7 @@ class DartDevelopmentServiceImpl implements DartDevelopmentService { + clientManager.clients.keyOf(client); + + bool get authCodesEnabled => _authCodesEnabled; +- final bool _authCodesEnabled; ++ bool _authCodesEnabled; + String? get authCode => _authCode; + String? _authCode; + +diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn +index 13bfd0a724b..042b6a45f7e 100644 +--- a/runtime/BUILD.gn ++++ b/runtime/BUILD.gn +@@ -87,7 +87,10 @@ config("dart_precompiler_config") { + + config("dart_os_config") { + defines = [] +- ++ if (target_os == "linux") { ++ target_os = "ohos" ++ } ++ print("DART TARGET OS: "+target_os ) + if (target_os == "android") { + defines += [ "DART_TARGET_OS_ANDROID" ] + } else if (target_os == "fuchsia") { +@@ -97,6 +100,9 @@ config("dart_os_config") { + defines += [ "DART_TARGET_OS_MACOS_IOS" ] + } else if (target_os == "linux") { + defines += [ "DART_TARGET_OS_LINUX" ] ++ } else if (target_os == "ohos") { ++ defines += [ "DART_TARGET_OS_OHOS" ] ++ print("DART TARGET OS OHOS") + } else if (target_os == "mac") { + defines += [ "DART_TARGET_OS_MACOS" ] + } else if (target_os == "win") { +@@ -221,6 +227,7 @@ config("dart_config") { + "-ggdb3", + "-fno-rtti", + "-fno-exceptions", ++ "-Wno-sign-compare" + ] + if (is_clang) { + cflags += [ +diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn +index f597fa5b0c3..5eecfb63c08 100644 +--- a/runtime/bin/BUILD.gn ++++ b/runtime/bin/BUILD.gn +@@ -32,6 +32,14 @@ config("libdart_builtin_config") { + "log", + ] + } ++ ++ print("dart runtime is_win:%b" ,is_win) ++ print("dart runtime is_ohos:%b" , is_ohos) ++ if(is_ohos && !is_win){ ++ libs +=[ ++ "hilog_ndk.z" ++ ] ++ } + } + + template("build_libdart_builtin") { +@@ -452,7 +460,7 @@ template("dart_io") { + ] + } + +- if (is_linux || is_win || is_fuchsia) { ++ if (is_linux || is_win || is_fuchsia || is_ohos) { + if (dart_use_fallback_root_certificates) { + sources += [ "//third_party/root_certificates/root_certificates.cc" ] + } else { +@@ -1036,7 +1044,7 @@ shared_library("entrypoints_verification_test") { + # The only effect of DART_SHARED_LIB is to export the Dart API. + "DART_SHARED_LIB", + ] +- if (is_linux || is_android) { ++ if (is_linux || is_android || (is_ohos || !is_win)) { + cflags = [ "-fPIC" ] + } + if (is_win) { +@@ -1091,7 +1099,7 @@ shared_library("ffi_test_functions") { + # The only effect of DART_SHARED_LIB is to export the Dart API. + "DART_SHARED_LIB", + ] +- if (is_linux || is_android) { ++ if (is_linux || is_android || is_ohos) { + cflags = [ "-fPIC" ] + } + if (is_win) { +diff --git a/runtime/bin/analyze_snapshot.cc b/runtime/bin/analyze_snapshot.cc +index 1e6f9a3bae2..557b9db7bf4 100644 +--- a/runtime/bin/analyze_snapshot.cc ++++ b/runtime/bin/analyze_snapshot.cc +@@ -10,7 +10,7 @@ + #include "bin/platform.h" + + #if defined(TARGET_ARCH_IS_64_BIT) && defined(DART_PRECOMPILED_RUNTIME) && \ +- (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX)) ++ (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_OHOS)) + #define SUPPORT_ANALYZE_SNAPSHOT + #endif + +diff --git a/runtime/bin/builtin_impl_sources.gni b/runtime/bin/builtin_impl_sources.gni +index feb1ee80a3b..d93fa28e4ee 100644 +--- a/runtime/bin/builtin_impl_sources.gni ++++ b/runtime/bin/builtin_impl_sources.gni +@@ -21,6 +21,7 @@ builtin_impl_sources = [ + "directory_android.cc", + "directory_fuchsia.cc", + "directory_linux.cc", ++ "directory_ohos.cc", + "directory_macos.cc", + "directory_win.cc", + "exe_utils.cc", +diff --git a/runtime/bin/builtin_natives.cc b/runtime/bin/builtin_natives.cc +index 14762ed7e63..5e70d68bfc6 100644 +--- a/runtime/bin/builtin_natives.cc ++++ b/runtime/bin/builtin_natives.cc +@@ -18,6 +18,13 @@ + #include "bin/io_natives.h" + #include "bin/platform.h" + ++#ifdef DART_TARGET_OS_OHOS ++#ifndef DART_HOST_OS_WINDOWS ++//#include ++//#include ++#endif ++#endif ++ + namespace dart { + namespace bin { + +@@ -106,6 +113,15 @@ void FUNCTION_NAME(Builtin_PrintString)(Dart_NativeArguments args) { + sizeof(newline)); + ASSERT(res == nullptr); + } ++ ++#ifdef DART_TARGET_OS_OHOS ++#ifndef DART_HOST_OS_WINDOWS ++ // { ++ // pthread_t thread = pthread_self(); ++ // OH_LOG_Print(LOG_APP,LOG_INFO,LOG_DOMAIN,"XComDartVm","Thread:%{public}lu %{public}s",thread,chars) ; ++ // } ++#endif ++#endif + } + + } // namespace bin +diff --git a/runtime/bin/crypto_ohos.cc b/runtime/bin/crypto_ohos.cc +new file mode 100644 +index 00000000000..b7aca695fb7 +--- /dev/null ++++ b/runtime/bin/crypto_ohos.cc +@@ -0,0 +1,44 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/crypto.h" ++#include "bin/fdutils.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool Crypto::GetRandomBytes(intptr_t count, uint8_t* buffer) { ++ ThreadSignalBlocker signal_blocker(SIGPROF); ++ intptr_t fd = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ open("/dev/urandom", O_RDONLY | O_CLOEXEC)); ++ if (fd < 0) { ++ return false; ++ } ++ intptr_t bytes_read = 0; ++ do { ++ int res = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ read(fd, buffer + bytes_read, count - bytes_read)); ++ if (res < 0) { ++ int err = errno; ++ close(fd); ++ errno = err; ++ return false; ++ } ++ bytes_read += res; ++ } while (bytes_read < count); ++ close(fd); ++ return true; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/directory_linux.cc b/runtime/bin/directory_linux.cc +index d2af2c50dad..d4e9082b9f0 100644 +--- a/runtime/bin/directory_linux.cc ++++ b/runtime/bin/directory_linux.cc +@@ -413,6 +413,10 @@ bool Directory::Create(Namespace* namespc, const char* dir_name) { + } + + const char* Directory::SystemTemp(Namespace* namespc) { ++ if (Directory::system_temp_path_override_ != NULL) { ++ return DartUtils::ScopedCopyCString(Directory::system_temp_path_override_); ++ } ++ + PathBuffer path; + const char* temp_dir = getenv("TMPDIR"); + if (temp_dir == NULL) { +diff --git a/runtime/bin/directory_ohos.cc b/runtime/bin/directory_ohos.cc +new file mode 100644 +index 00000000000..b5421969b61 +--- /dev/null ++++ b/runtime/bin/directory_ohos.cc +@@ -0,0 +1,513 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/directory.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/crypto.h" ++#include "bin/dartutils.h" ++#include "bin/fdutils.h" ++#include "bin/file.h" ++#include "bin/namespace.h" ++#include "bin/platform.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++PathBuffer::PathBuffer() : length_(0) { ++ data_ = calloc(PATH_MAX + 1, sizeof(char)); // NOLINT ++} ++ ++PathBuffer::~PathBuffer() { ++ free(data_); ++} ++ ++bool PathBuffer::AddW(const wchar_t* name) { ++ UNREACHABLE(); ++ return false; ++} ++ ++char* PathBuffer::AsString() const { ++ return reinterpret_cast(data_); ++} ++ ++wchar_t* PathBuffer::AsStringW() const { ++ UNREACHABLE(); ++ return NULL; ++} ++ ++const char* PathBuffer::AsScopedString() const { ++ return DartUtils::ScopedCopyCString(AsString()); ++} ++ ++bool PathBuffer::Add(const char* name) { ++ char* data = AsString(); ++ int written = snprintf(data + length_, PATH_MAX - length_, "%s", name); ++ data[PATH_MAX] = '\0'; ++ if ((written <= PATH_MAX - length_) && (written >= 0) && ++ (static_cast(written) == strnlen(name, PATH_MAX + 1))) { ++ length_ += written; ++ return true; ++ } else { ++ errno = ENAMETOOLONG; ++ return false; ++ } ++} ++ ++void PathBuffer::Reset(intptr_t new_length) { ++ length_ = new_length; ++ AsString()[length_] = '\0'; ++} ++ ++// A linked list of symbolic links, with their unique file system identifiers. ++// These are scanned to detect loops while doing a recursive directory listing. ++struct LinkList { ++ dev_t dev; ++ ino64_t ino; ++ LinkList* next; ++}; ++ ++ListType DirectoryListingEntry::Next(DirectoryListing* listing) { ++ if (done_) { ++ return kListDone; ++ } ++ ++ if (fd_ == -1) { ++ ASSERT(lister_ == 0); ++ NamespaceScope ns(listing->namespc(), listing->path_buffer().AsString()); ++ const int listingfd = ++ TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_DIRECTORY)); ++ if (listingfd < 0) { ++ done_ = true; ++ return kListError; ++ } ++ fd_ = listingfd; ++ } ++ ++ if (lister_ == 0) { ++ do { ++ lister_ = reinterpret_cast(fdopendir(fd_)); ++ } while ((lister_ == 0) && (errno == EINTR)); ++ if (lister_ == 0) { ++ done_ = true; ++ return kListError; ++ } ++ if (parent_ != NULL) { ++ if (!listing->path_buffer().Add(File::PathSeparator())) { ++ return kListError; ++ } ++ } ++ path_length_ = listing->path_buffer().length(); ++ } ++ // Reset. ++ listing->path_buffer().Reset(path_length_); ++ ResetLink(); ++ ++ // Iterate the directory and post the directories and files to the ++ // ports. ++ errno = 0; ++ dirent* entry = readdir(reinterpret_cast(lister_)); ++ if (entry != NULL) { ++ if (!listing->path_buffer().Add(entry->d_name)) { ++ done_ = true; ++ return kListError; ++ } ++ switch (entry->d_type) { ++ case DT_DIR: ++ if ((strcmp(entry->d_name, ".") == 0) || ++ (strcmp(entry->d_name, "..") == 0)) { ++ return Next(listing); ++ } ++ return kListDirectory; ++ case DT_BLK: ++ case DT_CHR: ++ case DT_FIFO: ++ case DT_SOCK: ++ case DT_REG: ++ return kListFile; ++ case DT_LNK: ++ if (!listing->follow_links()) { ++ return kListLink; ++ } ++ // Else fall through to next case. ++ FALL_THROUGH; ++ case DT_UNKNOWN: { ++ // On some file systems the entry type is not determined by ++ // readdir. For those and for links we use stat to determine ++ // the actual entry type. Notice that stat returns the type of ++ // the file pointed to. ++ NamespaceScope ns(listing->namespc(), ++ listing->path_buffer().AsString()); ++ struct stat64 entry_info; ++ int stat_success; ++ stat_success = TEMP_FAILURE_RETRY( ++ fstatat64(ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW)); ++ if (stat_success == -1) { ++ return kListError; ++ } ++ if (listing->follow_links() && S_ISLNK(entry_info.st_mode)) { ++ // Check to see if we are in a loop created by a symbolic link. ++ LinkList current_link = {entry_info.st_dev, entry_info.st_ino, link_}; ++ LinkList* previous = link_; ++ while (previous != NULL) { ++ if ((previous->dev == current_link.dev) && ++ (previous->ino == current_link.ino)) { ++ // Report the looping link as a link, rather than following it. ++ return kListLink; ++ } ++ previous = previous->next; ++ } ++ stat_success = ++ TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0)); ++ if (stat_success == -1 || (S_IFMT & entry_info.st_mode) == 0) { ++ // Report a broken link as a link, even if follow_links is true. ++ // A symbolic link can potentially point to an anon_inode. For ++ // example, an epoll file descriptor will have a symbolic link whose ++ // content is the string anon_inode:[eventpoll]. In this case, the ++ // target doesn't belong to any regular file catogory. ++ return kListLink; ++ } ++ if (S_ISDIR(entry_info.st_mode)) { ++ // Recurse into the subdirectory with current_link added to the ++ // linked list of seen file system links. ++ link_ = new LinkList(current_link); ++ if ((strcmp(entry->d_name, ".") == 0) || ++ (strcmp(entry->d_name, "..") == 0)) { ++ return Next(listing); ++ } ++ return kListDirectory; ++ } ++ } ++ if (S_ISDIR(entry_info.st_mode)) { ++ if ((strcmp(entry->d_name, ".") == 0) || ++ (strcmp(entry->d_name, "..") == 0)) { ++ return Next(listing); ++ } ++ return kListDirectory; ++ } else if (S_ISLNK(entry_info.st_mode)) { ++ return kListLink; ++ } else { ++ // Regular files, character devices, block devices, fifos, sockets and ++ // unknown types are all considered as files. ++ return kListFile; ++ } ++ } ++ ++ default: ++ // We should have covered all the bases. If not, let's get an error. ++ FATAL1("Unexpected d_type: %d\n", entry->d_type); ++ return kListError; ++ } ++ } ++ done_ = true; ++ ++ if (errno != 0) { ++ return kListError; ++ } ++ ++ return kListDone; ++} ++ ++DirectoryListingEntry::~DirectoryListingEntry() { ++ ResetLink(); ++ if (lister_ != 0) { ++ // This also closes fd_. ++ VOID_NO_RETRY_EXPECTED(closedir(reinterpret_cast(lister_))); ++ } ++} ++ ++void DirectoryListingEntry::ResetLink() { ++ if ((link_ != NULL) && ((parent_ == NULL) || (parent_->link_ != link_))) { ++ delete link_; ++ link_ = NULL; ++ } ++ if (parent_ != NULL) { ++ link_ = parent_->link_; ++ } ++} ++ ++static bool DeleteRecursively(int dirfd, PathBuffer* path); ++ ++static bool DeleteFile(int dirfd, char* file_name, PathBuffer* path) { ++ return path->Add(file_name) && ++ (NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), 0)) == 0); ++} ++ ++static bool DeleteDir(int dirfd, char* dir_name, PathBuffer* path) { ++ if ((strcmp(dir_name, ".") == 0) || (strcmp(dir_name, "..") == 0)) { ++ return true; ++ } ++ return path->Add(dir_name) && DeleteRecursively(dirfd, path); ++} ++ ++static bool DeleteRecursively(int dirfd, PathBuffer* path) { ++ // Do not recurse into links for deletion. Instead delete the link. ++ // If it's a file, delete it. ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY( ++ fstatat64(dirfd, path->AsString(), &st, AT_SYMLINK_NOFOLLOW)) == -1) { ++ return false; ++ } else if (!S_ISDIR(st.st_mode)) { ++ return (NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), 0)) == 0); ++ } ++ ++ if (!path->Add(File::PathSeparator())) { ++ return false; ++ } ++ ++ // Not a link. Attempt to open as a directory and recurse into the ++ // directory. ++ const int fd = ++ TEMP_FAILURE_RETRY(openat64(dirfd, path->AsString(), O_DIRECTORY)); ++ if (fd < 0) { ++ return false; ++ } ++ DIR* dir_pointer; ++ do { ++ dir_pointer = fdopendir(fd); ++ } while ((dir_pointer == NULL) && (errno == EINTR)); ++ if (dir_pointer == NULL) { ++ FDUtils::SaveErrorAndClose(fd); ++ return false; ++ } ++ ++ // Iterate the directory and delete all files and directories. ++ int path_length = path->length(); ++ while (true) { ++ // In case `readdir()` returns `NULL` we distinguish between end-of-stream ++ // and error by looking if `errno` was updated. ++ errno = 0; ++ // In glibc 2.24+, readdir_r is deprecated. ++ // According to the man page for readdir: ++ // "readdir(3) is not required to be thread-safe. However, in modern ++ // implementations (including the glibc implementation), concurrent calls to ++ // readdir(3) that specify different directory streams are thread-safe." ++ dirent* entry = readdir(dir_pointer); ++ if (entry == NULL) { ++ // Failed to read next directory entry. ++ if (errno != 0) { ++ break; ++ } ++ // End of directory. ++ int status = NO_RETRY_EXPECTED(closedir(dir_pointer)); ++ if (status != 0) { ++ return false; ++ } ++ status = ++ NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), AT_REMOVEDIR)); ++ return status == 0; ++ } ++ bool ok = false; ++ switch (entry->d_type) { ++ case DT_DIR: ++ ok = DeleteDir(dirfd, entry->d_name, path); ++ break; ++ case DT_BLK: ++ case DT_CHR: ++ case DT_FIFO: ++ case DT_SOCK: ++ case DT_REG: ++ case DT_LNK: ++ // Treat all links as files. This will delete the link which ++ // is what we want no matter if the link target is a file or a ++ // directory. ++ ok = DeleteFile(dirfd, entry->d_name, path); ++ break; ++ case DT_UNKNOWN: { ++ if (!path->Add(entry->d_name)) { ++ break; ++ } ++ // On some file systems the entry type is not determined by ++ // readdir. For those we use lstat to determine the entry ++ // type. ++ struct stat64 entry_info; ++ if (TEMP_FAILURE_RETRY(fstatat64(dirfd, path->AsString(), &entry_info, ++ AT_SYMLINK_NOFOLLOW)) == -1) { ++ break; ++ } ++ path->Reset(path_length); ++ if (S_ISDIR(entry_info.st_mode)) { ++ ok = DeleteDir(dirfd, entry->d_name, path); ++ } else { ++ // Treat links as files. This will delete the link which is ++ // what we want no matter if the link target is a file or a ++ // directory. ++ ok = DeleteFile(dirfd, entry->d_name, path); ++ } ++ break; ++ } ++ default: ++ // We should have covered all the bases. If not, let's get an error. ++ FATAL1("Unexpected d_type: %d\n", entry->d_type); ++ break; ++ } ++ if (!ok) { ++ break; ++ } ++ path->Reset(path_length); ++ } ++ // Only happens if an error. ++ ASSERT(errno != 0); ++ int err = errno; ++ VOID_NO_RETRY_EXPECTED(closedir(dir_pointer)); ++ errno = err; ++ return false; ++} ++ ++Directory::ExistsResult Directory::Exists(Namespace* namespc, ++ const char* dir_name) { ++ NamespaceScope ns(namespc, dir_name); ++ struct stat64 entry_info; ++ int success = ++ TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0)); ++ if (success == 0) { ++ if (S_ISDIR(entry_info.st_mode)) { ++ return EXISTS; ++ } else { ++ // An OSError may be constructed based on the return value of this ++ // function, so set errno to something that makes sense. ++ errno = ENOTDIR; ++ return DOES_NOT_EXIST; ++ } ++ } else { ++ if ((errno == EACCES) || (errno == EBADF) || (errno == EFAULT) || ++ (errno == ENOMEM) || (errno == EOVERFLOW)) { ++ // Search permissions denied for one of the directories in the ++ // path or a low level error occurred. We do not know if the ++ // directory exists. ++ return UNKNOWN; ++ } ++ ASSERT((errno == ELOOP) || (errno == ENAMETOOLONG) || (errno == ENOENT) || ++ (errno == ENOTDIR)); ++ return DOES_NOT_EXIST; ++ } ++} ++ ++char* Directory::CurrentNoScope() { ++ return getcwd(NULL, 0); ++} ++ ++bool Directory::Create(Namespace* namespc, const char* dir_name) { ++ NamespaceScope ns(namespc, dir_name); ++ // Create the directory with the permissions specified by the ++ // process umask. ++ const int result = NO_RETRY_EXPECTED(mkdirat(ns.fd(), ns.path(), 0777)); ++ // If the directory already exists, treat it as a success. ++ if ((result == -1) && (errno == EEXIST)) { ++ return (Exists(namespc, dir_name) == EXISTS); ++ } ++ return (result == 0); ++} ++ ++const char* Directory::SystemTemp(Namespace* namespc) { ++ if (Directory::system_temp_path_override_ != NULL) { ++ return DartUtils::ScopedCopyCString(Directory::system_temp_path_override_); ++ } ++ ++ PathBuffer path; ++ const char* temp_dir = getenv("TMPDIR"); ++ if (temp_dir == NULL) { ++ temp_dir = getenv("TMP"); ++ } ++ if (temp_dir == NULL) { ++ temp_dir = "/tmp"; ++ } ++ NamespaceScope ns(namespc, temp_dir); ++ if (!path.Add(ns.path())) { ++ return NULL; ++ } ++ ++ // Remove any trailing slash. ++ char* result = path.AsString(); ++ int length = strlen(result); ++ if ((length > 1) && (result[length - 1] == '/')) { ++ result[length - 1] = '\0'; ++ } ++ return path.AsScopedString(); ++} ++ ++// Returns a new, unused directory name, adding characters to the end ++// of prefix. Creates the directory with the permissions specified ++// by the process umask. ++// The return value is Dart_ScopeAllocated. ++const char* Directory::CreateTemp(Namespace* namespc, const char* prefix) { ++ PathBuffer path; ++ const int firstchar = 'A'; ++ const int numchars = 'Z' - 'A' + 1; ++ uint8_t random_bytes[7]; ++ ++ // mkdtemp doesn't have an "at" variant, so we have to simulate it. ++ if (!path.Add(prefix)) { ++ return NULL; ++ } ++ intptr_t prefix_length = path.length(); ++ while (true) { ++ Crypto::GetRandomBytes(6, random_bytes); ++ for (intptr_t i = 0; i < 6; i++) { ++ random_bytes[i] = (random_bytes[i] % numchars) + firstchar; ++ } ++ random_bytes[6] = '\0'; ++ if (!path.Add(reinterpret_cast(random_bytes))) { ++ return NULL; ++ } ++ NamespaceScope ns(namespc, path.AsString()); ++ const int result = NO_RETRY_EXPECTED(mkdirat(ns.fd(), ns.path(), 0777)); ++ if (result == 0) { ++ return path.AsScopedString(); ++ } else if (errno == EEXIST) { ++ path.Reset(prefix_length); ++ } else { ++ return NULL; ++ } ++ } ++} ++ ++bool Directory::Delete(Namespace* namespc, ++ const char* dir_name, ++ bool recursive) { ++ NamespaceScope ns(namespc, dir_name); ++ if (!recursive) { ++ if ((File::GetType(namespc, dir_name, false) == File::kIsLink) && ++ (File::GetType(namespc, dir_name, true) == File::kIsDirectory)) { ++ return NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0; ++ } ++ return NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), AT_REMOVEDIR)) == 0; ++ } else { ++ PathBuffer path; ++ if (!path.Add(ns.path())) { ++ return false; ++ } ++ return DeleteRecursively(ns.fd(), &path); ++ } ++} ++ ++bool Directory::Rename(Namespace* namespc, ++ const char* old_path, ++ const char* new_path) { ++ ExistsResult exists = Exists(namespc, old_path); ++ if (exists != EXISTS) { ++ return false; ++ } ++ NamespaceScope oldns(namespc, old_path); ++ NamespaceScope newns(namespc, new_path); ++ return (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(), ++ newns.path())) == 0); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/eventhandler_ohos.cc b/runtime/bin/eventhandler_ohos.cc +new file mode 100644 +index 00000000000..20204a1df3b +--- /dev/null ++++ b/runtime/bin/eventhandler_ohos.cc +@@ -0,0 +1,443 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/eventhandler.h" ++#include "bin/eventhandler_ohos.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/dartutils.h" ++#include "bin/fdutils.h" ++#include "bin/lockers.h" ++#include "bin/process.h" ++#include "bin/socket.h" ++#include "bin/thread.h" ++#include "platform/syslog.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++intptr_t DescriptorInfo::GetPollEvents() { ++ // Do not ask for EPOLLERR and EPOLLHUP explicitly as they are ++ // triggered anyway. ++ intptr_t events = 0; ++ if ((Mask() & (1 << kInEvent)) != 0) { ++ events |= EPOLLIN; ++ } ++ if ((Mask() & (1 << kOutEvent)) != 0) { ++ events |= EPOLLOUT; ++ } ++ return events; ++} ++ ++// Unregister the file descriptor for a DescriptorInfo structure with ++// epoll. ++static void RemoveFromEpollInstance(intptr_t epoll_fd_, DescriptorInfo* di) { ++ VOID_NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, di->fd(), NULL)); ++} ++ ++static void AddToEpollInstance(intptr_t epoll_fd_, DescriptorInfo* di) { ++ struct epoll_event event; ++ event.events = EPOLLRDHUP | di->GetPollEvents(); ++ if (!di->IsListeningSocket()) { ++ event.events |= EPOLLET; ++ } ++ event.data.ptr = di; ++ int status = ++ NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, di->fd(), &event)); ++ if (status == -1) { ++ // TODO(dart:io): Verify that the dart end is handling this correctly. ++ ++ // Epoll does not accept the file descriptor. It could be due to ++ // already closed file descriptor, or unuspported devices, such ++ // as /dev/null. In such case, mark the file descriptor as closed, ++ // so dart will handle it accordingly. ++ di->NotifyAllDartPorts(1 << kCloseEvent); ++ } ++} ++ ++EventHandlerImplementation::EventHandlerImplementation() ++ : socket_map_(&SimpleHashMap::SamePointerValue, 16) { ++ intptr_t result; ++ result = NO_RETRY_EXPECTED(pipe(interrupt_fds_)); ++ if (result != 0) { ++ FATAL("Pipe creation failed"); ++ } ++ if (!FDUtils::SetNonBlocking(interrupt_fds_[0])) { ++ FATAL("Failed to set pipe fd non blocking\n"); ++ } ++ if (!FDUtils::SetCloseOnExec(interrupt_fds_[0])) { ++ FATAL("Failed to set pipe fd close on exec\n"); ++ } ++ if (!FDUtils::SetCloseOnExec(interrupt_fds_[1])) { ++ FATAL("Failed to set pipe fd close on exec\n"); ++ } ++ shutdown_ = false; ++ // The initial size passed to epoll_create is ignore on newer (>= ++ // 2.6.8) Linux versions ++ static const int kEpollInitialSize = 64; ++ epoll_fd_ = NO_RETRY_EXPECTED(epoll_create(kEpollInitialSize)); ++ if (epoll_fd_ == -1) { ++ FATAL1("Failed creating epoll file descriptor: %i", errno); ++ } ++ if (!FDUtils::SetCloseOnExec(epoll_fd_)) { ++ FATAL("Failed to set epoll fd close on exec\n"); ++ } ++ // Register the interrupt_fd with the epoll instance. ++ struct epoll_event event; ++ event.events = EPOLLIN; ++ event.data.ptr = NULL; ++ int status = NO_RETRY_EXPECTED( ++ epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupt_fds_[0], &event)); ++ if (status == -1) { ++ FATAL("Failed adding interrupt fd to epoll instance"); ++ } ++ timer_fd_ = NO_RETRY_EXPECTED(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)); ++ if (timer_fd_ == -1) { ++ FATAL1("Failed creating timerfd file descriptor: %i", errno); ++ } ++ // Register the timer_fd_ with the epoll instance. ++ event.events = EPOLLIN; ++ event.data.fd = timer_fd_; ++ status = ++ NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &event)); ++ if (status == -1) { ++ FATAL2("Failed adding timerfd fd(%i) to epoll instance: %i", timer_fd_, ++ errno); ++ } ++} ++ ++static void DeleteDescriptorInfo(void* info) { ++ DescriptorInfo* di = reinterpret_cast(info); ++ di->Close(); ++ delete di; ++} ++ ++EventHandlerImplementation::~EventHandlerImplementation() { ++ socket_map_.Clear(DeleteDescriptorInfo); ++ close(epoll_fd_); ++ close(timer_fd_); ++ close(interrupt_fds_[0]); ++ close(interrupt_fds_[1]); ++} ++ ++void EventHandlerImplementation::UpdateEpollInstance(intptr_t old_mask, ++ DescriptorInfo* di) { ++ intptr_t new_mask = di->Mask(); ++ if ((old_mask != 0) && (new_mask == 0)) { ++ RemoveFromEpollInstance(epoll_fd_, di); ++ } else if ((old_mask == 0) && (new_mask != 0)) { ++ AddToEpollInstance(epoll_fd_, di); ++ } else if ((old_mask != 0) && (new_mask != 0) && (old_mask != new_mask)) { ++ ASSERT(!di->IsListeningSocket()); ++ RemoveFromEpollInstance(epoll_fd_, di); ++ AddToEpollInstance(epoll_fd_, di); ++ } ++} ++ ++DescriptorInfo* EventHandlerImplementation::GetDescriptorInfo( ++ intptr_t fd, ++ bool is_listening) { ++ ASSERT(fd >= 0); ++ SimpleHashMap::Entry* entry = socket_map_.Lookup( ++ GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd), true); ++ ASSERT(entry != NULL); ++ DescriptorInfo* di = reinterpret_cast(entry->value); ++ if (di == NULL) { ++ // If there is no data in the hash map for this file descriptor a ++ // new DescriptorInfo for the file descriptor is inserted. ++ if (is_listening) { ++ di = new DescriptorInfoMultiple(fd); ++ } else { ++ di = new DescriptorInfoSingle(fd); ++ } ++ entry->value = di; ++ } ++ ASSERT(fd == di->fd()); ++ return di; ++} ++ ++void EventHandlerImplementation::WakeupHandler(intptr_t id, ++ Dart_Port dart_port, ++ int64_t data) { ++ InterruptMessage msg; ++ msg.id = id; ++ msg.dart_port = dart_port; ++ msg.data = data; ++ // WriteToBlocking will write up to 512 bytes atomically, and since our msg ++ // is smaller than 512, we don't need a thread lock. ++ // See: http://linux.die.net/man/7/pipe, section 'Pipe_buf'. ++ ASSERT(kInterruptMessageSize < PIPE_BUF); ++ intptr_t result = ++ FDUtils::WriteToBlocking(interrupt_fds_[1], &msg, kInterruptMessageSize); ++ if (result != kInterruptMessageSize) { ++ if (result == -1) { ++ perror("Interrupt message failure:"); ++ } ++ FATAL1("Interrupt message failure. Wrote %" Pd " bytes.", result); ++ } ++} ++ ++void EventHandlerImplementation::HandleInterruptFd() { ++ const intptr_t MAX_MESSAGES = kInterruptMessageSize; ++ InterruptMessage msg[MAX_MESSAGES]; ++ ssize_t bytes = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ read(interrupt_fds_[0], msg, MAX_MESSAGES * kInterruptMessageSize)); ++ for (ssize_t i = 0; i < bytes / kInterruptMessageSize; i++) { ++ if (msg[i].id == kTimerId) { ++ timeout_queue_.UpdateTimeout(msg[i].dart_port, msg[i].data); ++ UpdateTimerFd(); ++ } else if (msg[i].id == kShutdownId) { ++ shutdown_ = true; ++ } else { ++ ASSERT((msg[i].data & COMMAND_MASK) != 0); ++ Socket* socket = reinterpret_cast(msg[i].id); ++ RefCntReleaseScope rs(socket); ++ if (socket->fd() == -1) { ++ continue; ++ } ++ DescriptorInfo* di = ++ GetDescriptorInfo(socket->fd(), IS_LISTENING_SOCKET(msg[i].data)); ++ if (IS_COMMAND(msg[i].data, kShutdownReadCommand)) { ++ ASSERT(!di->IsListeningSocket()); ++ // Close the socket for reading. ++ VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_RD)); ++ } else if (IS_COMMAND(msg[i].data, kShutdownWriteCommand)) { ++ ASSERT(!di->IsListeningSocket()); ++ // Close the socket for writing. ++ VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_WR)); ++ } else if (IS_COMMAND(msg[i].data, kCloseCommand)) { ++ // Close the socket and free system resources and move on to next ++ // message. ++ if (IS_SIGNAL_SOCKET(msg[i].data)) { ++ Process::ClearSignalHandlerByFd(di->fd(), socket->isolate_port()); ++ } ++ intptr_t old_mask = di->Mask(); ++ Dart_Port port = msg[i].dart_port; ++ if (port != ILLEGAL_PORT) { ++ di->RemovePort(port); ++ } ++ intptr_t new_mask = di->Mask(); ++ UpdateEpollInstance(old_mask, di); ++ ++ intptr_t fd = di->fd(); ++ ASSERT(fd == socket->fd()); ++ if (di->IsListeningSocket()) { ++ // We only close the socket file descriptor from the operating ++ // system if there are no other dart socket objects which ++ // are listening on the same (address, port) combination. ++ ListeningSocketRegistry* registry = ++ ListeningSocketRegistry::Instance(); ++ ++ MutexLocker locker(registry->mutex()); ++ ++ if (registry->CloseSafe(socket)) { ++ ASSERT(new_mask == 0); ++ socket_map_.Remove(GetHashmapKeyFromFd(fd), ++ GetHashmapHashFromFd(fd)); ++ di->Close(); ++ delete di; ++ } ++ socket->CloseFd(); ++ } else { ++ ASSERT(new_mask == 0); ++ socket_map_.Remove(GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd)); ++ di->Close(); ++ delete di; ++ socket->CloseFd(); ++ } ++ DartUtils::PostInt32(port, 1 << kDestroyedEvent); ++ } else if (IS_COMMAND(msg[i].data, kReturnTokenCommand)) { ++ int count = TOKEN_COUNT(msg[i].data); ++ intptr_t old_mask = di->Mask(); ++ di->ReturnTokens(msg[i].dart_port, count); ++ UpdateEpollInstance(old_mask, di); ++ } else if (IS_COMMAND(msg[i].data, kSetEventMaskCommand)) { ++ // `events` can only have kInEvent/kOutEvent flags set. ++ intptr_t events = msg[i].data & EVENT_MASK; ++ ASSERT(0 == (events & ~(1 << kInEvent | 1 << kOutEvent))); ++ ++ intptr_t old_mask = di->Mask(); ++ di->SetPortAndMask(msg[i].dart_port, msg[i].data & EVENT_MASK); ++ UpdateEpollInstance(old_mask, di); ++ } else { ++ UNREACHABLE(); ++ } ++ } ++ } ++} ++ ++void EventHandlerImplementation::UpdateTimerFd() { ++ struct itimerspec it; ++ memset(&it, 0, sizeof(it)); ++ if (timeout_queue_.HasTimeout()) { ++ int64_t millis = timeout_queue_.CurrentTimeout(); ++ it.it_value.tv_sec = millis / 1000; ++ it.it_value.tv_nsec = (millis % 1000) * 1000000; ++ } ++ VOID_NO_RETRY_EXPECTED( ++ timerfd_settime(timer_fd_, TFD_TIMER_ABSTIME, &it, NULL)); ++} ++ ++#ifdef DEBUG_POLL ++static void PrintEventMask(intptr_t fd, intptr_t events) { ++ Syslog::Print("%d ", fd); ++ if ((events & EPOLLIN) != 0) { ++ Syslog::Print("EPOLLIN "); ++ } ++ if ((events & EPOLLPRI) != 0) { ++ Syslog::Print("EPOLLPRI "); ++ } ++ if ((events & EPOLLOUT) != 0) { ++ Syslog::Print("EPOLLOUT "); ++ } ++ if ((events & EPOLLERR) != 0) { ++ Syslog::Print("EPOLLERR "); ++ } ++ if ((events & EPOLLHUP) != 0) { ++ Syslog::Print("EPOLLHUP "); ++ } ++ if ((events & EPOLLRDHUP) != 0) { ++ Syslog::Print("EPOLLRDHUP "); ++ } ++ int all_events = ++ EPOLLIN | EPOLLPRI | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDHUP; ++ if ((events & ~all_events) != 0) { ++ Syslog::Print("(and %08x) ", events & ~all_events); ++ } ++ Syslog::Print("(available %d) ", FDUtils::AvailableBytes(fd)); ++ ++ Syslog::Print("\n"); ++} ++#endif ++ ++intptr_t EventHandlerImplementation::GetPollEvents(intptr_t events, ++ DescriptorInfo* di) { ++#ifdef DEBUG_POLL ++ PrintEventMask(di->fd(), events); ++#endif ++ if ((events & EPOLLERR) != 0) { ++ // Return error only if EPOLLIN is present. ++ return ((events & EPOLLIN) != 0) ? (1 << kErrorEvent) : 0; ++ } ++ intptr_t event_mask = 0; ++ if ((events & EPOLLIN) != 0) { ++ event_mask |= (1 << kInEvent); ++ } ++ if ((events & EPOLLOUT) != 0) { ++ event_mask |= (1 << kOutEvent); ++ } ++ if ((events & (EPOLLHUP | EPOLLRDHUP)) != 0) { ++ event_mask |= (1 << kCloseEvent); ++ } ++ return event_mask; ++} ++ ++void EventHandlerImplementation::HandleEvents(struct epoll_event* events, ++ int size) { ++ bool interrupt_seen = false; ++ for (int i = 0; i < size; i++) { ++ if (events[i].data.ptr == NULL) { ++ interrupt_seen = true; ++ } else if (events[i].data.fd == timer_fd_) { ++ int64_t val; ++ VOID_TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ read(timer_fd_, &val, sizeof(val))); ++ if (timeout_queue_.HasTimeout()) { ++ DartUtils::PostNull(timeout_queue_.CurrentPort()); ++ timeout_queue_.RemoveCurrent(); ++ } ++ UpdateTimerFd(); ++ } else { ++ DescriptorInfo* di = ++ reinterpret_cast(events[i].data.ptr); ++ const intptr_t old_mask = di->Mask(); ++ const intptr_t event_mask = GetPollEvents(events[i].events, di); ++ if ((event_mask & (1 << kErrorEvent)) != 0) { ++ di->NotifyAllDartPorts(event_mask); ++ UpdateEpollInstance(old_mask, di); ++ } else if (event_mask != 0) { ++ Dart_Port port = di->NextNotifyDartPort(event_mask); ++ ASSERT(port != 0); ++ UpdateEpollInstance(old_mask, di); ++ DartUtils::PostInt32(port, event_mask); ++ } ++ } ++ } ++ if (interrupt_seen) { ++ // Handle after socket events, so we avoid closing a socket before we handle ++ // the current events. ++ HandleInterruptFd(); ++ } ++} ++ ++void EventHandlerImplementation::Poll(uword args) { ++ ThreadSignalBlocker signal_blocker(SIGPROF); ++ static const intptr_t kMaxEvents = 16; ++ struct epoll_event events[kMaxEvents]; ++ EventHandler* handler = reinterpret_cast(args); ++ EventHandlerImplementation* handler_impl = &handler->delegate_; ++ ASSERT(handler_impl != NULL); ++ ++ while (!handler_impl->shutdown_) { ++ intptr_t result = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ epoll_wait(handler_impl->epoll_fd_, events, kMaxEvents, -1)); ++ ASSERT(EAGAIN == EWOULDBLOCK); ++ if (result <= 0) { ++ if (errno != EWOULDBLOCK) { ++ perror("Poll failed"); ++ } ++ } else { ++ handler_impl->HandleEvents(events, result); ++ } ++ } ++ DEBUG_ASSERT(ReferenceCounted::instances() == 0); ++ handler->NotifyShutdownDone(); ++} ++ ++void EventHandlerImplementation::Start(EventHandler* handler) { ++ int result = ++ Thread::Start("dart:io EventHandler", &EventHandlerImplementation::Poll, ++ reinterpret_cast(handler)); ++ if (result != 0) { ++ FATAL1("Failed to start event handler thread %d", result); ++ } ++} ++ ++void EventHandlerImplementation::Shutdown() { ++ SendData(kShutdownId, 0, 0); ++} ++ ++void EventHandlerImplementation::SendData(intptr_t id, ++ Dart_Port dart_port, ++ int64_t data) { ++ WakeupHandler(id, dart_port, data); ++} ++ ++void* EventHandlerImplementation::GetHashmapKeyFromFd(intptr_t fd) { ++ // The hashmap does not support keys with value 0. ++ return reinterpret_cast(fd + 1); ++} ++ ++uint32_t EventHandlerImplementation::GetHashmapHashFromFd(intptr_t fd) { ++ // The hashmap does not support keys with value 0. ++ return dart::Utils::WordHash(fd + 1); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/eventhandler_ohos.h b/runtime/bin/eventhandler_ohos.h +new file mode 100644 +index 00000000000..0e4d84c8c12 +--- /dev/null ++++ b/runtime/bin/eventhandler_ohos.h +@@ -0,0 +1,99 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#ifndef RUNTIME_BIN_EVENTHANDLER_OHOS_H_ ++#define RUNTIME_BIN_EVENTHANDLER_OHOS_H_ ++ ++#if !defined(RUNTIME_BIN_EVENTHANDLER_H_) ++#error Do not include eventhandler_linux.h directly; use eventhandler.h instead. ++#endif ++ ++#include ++#include ++#include ++#include ++ ++#include "platform/hashmap.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++class DescriptorInfo : public DescriptorInfoBase { ++ public: ++ explicit DescriptorInfo(intptr_t fd) : DescriptorInfoBase(fd) {} ++ ++ virtual ~DescriptorInfo() {} ++ ++ intptr_t GetPollEvents(); ++ ++ virtual void Close() { ++ close(fd_); ++ fd_ = -1; ++ } ++ ++ private: ++ DISALLOW_COPY_AND_ASSIGN(DescriptorInfo); ++}; ++ ++class DescriptorInfoSingle : public DescriptorInfoSingleMixin { ++ public: ++ explicit DescriptorInfoSingle(intptr_t fd) ++ : DescriptorInfoSingleMixin(fd, false) {} ++ virtual ~DescriptorInfoSingle() {} ++ ++ private: ++ DISALLOW_COPY_AND_ASSIGN(DescriptorInfoSingle); ++}; ++ ++class DescriptorInfoMultiple ++ : public DescriptorInfoMultipleMixin { ++ public: ++ explicit DescriptorInfoMultiple(intptr_t fd) ++ : DescriptorInfoMultipleMixin(fd, false) {} ++ virtual ~DescriptorInfoMultiple() {} ++ ++ private: ++ DISALLOW_COPY_AND_ASSIGN(DescriptorInfoMultiple); ++}; ++ ++class EventHandlerImplementation { ++ public: ++ EventHandlerImplementation(); ++ ~EventHandlerImplementation(); ++ ++ void UpdateEpollInstance(intptr_t old_mask, DescriptorInfo* di); ++ ++ // Gets the socket data structure for a given file ++ // descriptor. Creates a new one if one is not found. ++ DescriptorInfo* GetDescriptorInfo(intptr_t fd, bool is_listening); ++ void SendData(intptr_t id, Dart_Port dart_port, int64_t data); ++ void Start(EventHandler* handler); ++ void Shutdown(); ++ ++ private: ++ void HandleEvents(struct epoll_event* events, int size); ++ static void Poll(uword args); ++ void WakeupHandler(intptr_t id, Dart_Port dart_port, int64_t data); ++ void HandleInterruptFd(); ++ void UpdateTimerFd(); ++ void SetPort(intptr_t fd, Dart_Port dart_port, intptr_t mask); ++ intptr_t GetPollEvents(intptr_t events, DescriptorInfo* di); ++ static void* GetHashmapKeyFromFd(intptr_t fd); ++ static uint32_t GetHashmapHashFromFd(intptr_t fd); ++ ++ SimpleHashMap socket_map_; ++ TimeoutQueue timeout_queue_; ++ bool shutdown_; ++ int interrupt_fds_[2]; ++ int epoll_fd_; ++ int timer_fd_; ++ ++ DISALLOW_COPY_AND_ASSIGN(EventHandlerImplementation); ++}; ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // RUNTIME_BIN_EVENTHANDLER_OHOS_H_ +diff --git a/runtime/bin/fdutils_ohos.cc b/runtime/bin/fdutils_ohos.cc +new file mode 100644 +index 00000000000..f7531006cbe +--- /dev/null ++++ b/runtime/bin/fdutils_ohos.cc +@@ -0,0 +1,142 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/fdutils.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool FDUtils::SetCloseOnExec(intptr_t fd) { ++ intptr_t status; ++ status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFD)); ++ if (status < 0) { ++ perror("fcntl(F_GETFD) failed"); ++ return false; ++ } ++ status |= FD_CLOEXEC; ++ if (NO_RETRY_EXPECTED(fcntl(fd, F_SETFD, status)) < 0) { ++ perror("fcntl(F_SETFD, FD_CLOEXEC) failed"); ++ return false; ++ } ++ return true; ++} ++ ++static bool SetBlockingHelper(intptr_t fd, bool blocking) { ++ intptr_t status; ++ status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFL)); ++ if (status < 0) { ++ perror("fcntl(F_GETFL) failed"); ++ return false; ++ } ++ status = blocking ? (status & ~O_NONBLOCK) : (status | O_NONBLOCK); ++ if (NO_RETRY_EXPECTED(fcntl(fd, F_SETFL, status)) < 0) { ++ perror("fcntl(F_SETFL, O_NONBLOCK) failed"); ++ return false; ++ } ++ return true; ++} ++ ++bool FDUtils::SetNonBlocking(intptr_t fd) { ++ return SetBlockingHelper(fd, false); ++} ++ ++bool FDUtils::SetBlocking(intptr_t fd) { ++ return SetBlockingHelper(fd, true); ++} ++ ++bool FDUtils::IsBlocking(intptr_t fd, bool* is_blocking) { ++ intptr_t status; ++ status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFL)); ++ if (status < 0) { ++ return false; ++ } ++ *is_blocking = (status & O_NONBLOCK) == 0; ++ return true; ++} ++ ++intptr_t FDUtils::AvailableBytes(intptr_t fd) { ++ int available; // ioctl for FIONREAD expects an 'int*' argument. ++ int result = NO_RETRY_EXPECTED(ioctl(fd, FIONREAD, &available)); ++ if (result < 0) { ++ return result; ++ } ++ ASSERT(available >= 0); ++ return static_cast(available); ++} ++ ++ssize_t FDUtils::ReadFromBlocking(int fd, void* buffer, size_t count) { ++#ifdef DEBUG ++ bool is_blocking = false; ++ ASSERT(FDUtils::IsBlocking(fd, &is_blocking)); ++ ASSERT(is_blocking); ++#endif ++ size_t remaining = count; ++ char* buffer_pos = reinterpret_cast(buffer); ++ while (remaining > 0) { ++ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer_pos, remaining)); ++ if (bytes_read == 0) { ++ return count - remaining; ++ } else if (bytes_read == -1) { ++ ASSERT(EAGAIN == EWOULDBLOCK); ++ // Error code EWOULDBLOCK should only happen for non blocking ++ // file descriptors. ++ ASSERT(errno != EWOULDBLOCK); ++ return -1; ++ } else { ++ ASSERT(bytes_read > 0); ++ remaining -= bytes_read; ++ buffer_pos += bytes_read; ++ } ++ } ++ return count; ++} ++ ++ssize_t FDUtils::WriteToBlocking(int fd, const void* buffer, size_t count) { ++#ifdef DEBUG ++ bool is_blocking = false; ++ ASSERT(FDUtils::IsBlocking(fd, &is_blocking)); ++ ASSERT(is_blocking); ++#endif ++ size_t remaining = count; ++ char* buffer_pos = const_cast(reinterpret_cast(buffer)); ++ while (remaining > 0) { ++ ssize_t bytes_written = ++ TEMP_FAILURE_RETRY(write(fd, buffer_pos, remaining)); ++ if (bytes_written == 0) { ++ return count - remaining; ++ } else if (bytes_written == -1) { ++ ASSERT(EAGAIN == EWOULDBLOCK); ++ // Error code EWOULDBLOCK should only happen for non blocking ++ // file descriptors. ++ ASSERT(errno != EWOULDBLOCK); ++ return -1; ++ } else { ++ ASSERT(bytes_written > 0); ++ remaining -= bytes_written; ++ buffer_pos += bytes_written; ++ } ++ } ++ return count; ++} ++ ++void FDUtils::SaveErrorAndClose(intptr_t fd) { ++ int err = errno; ++ close(fd); ++ errno = err; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/ffi_unit_test/BUILD.gn b/runtime/bin/ffi_unit_test/BUILD.gn +index c06d9e57e9f..ae681704cc6 100644 +--- a/runtime/bin/ffi_unit_test/BUILD.gn ++++ b/runtime/bin/ffi_unit_test/BUILD.gn +@@ -73,6 +73,10 @@ config("define_target_os_linux") { + defines = [ "DART_TARGET_OS_LINUX" ] + } + ++config("define_target_os_ohos") { ++ defines = [ "DART_TARGET_OS_OHOS" ] ++} ++ + config("define_target_os_macos") { + defines = [ "DART_TARGET_OS_MACOS" ] + } +diff --git a/runtime/bin/file_ohos.cc b/runtime/bin/file_ohos.cc +new file mode 100644 +index 00000000000..225b1d62c67 +--- /dev/null ++++ b/runtime/bin/file_ohos.cc +@@ -0,0 +1,786 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/file.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/builtin.h" ++#include "bin/fdutils.h" ++#include "bin/namespace.h" ++#include "platform/signal_blocker.h" ++#include "platform/syslog.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++class FileHandle { ++ public: ++ explicit FileHandle(int fd) : fd_(fd) {} ++ ~FileHandle() {} ++ int fd() const { return fd_; } ++ void set_fd(int fd) { fd_ = fd; } ++ ++ private: ++ int fd_; ++ ++ DISALLOW_COPY_AND_ASSIGN(FileHandle); ++}; ++ ++File::~File() { ++ if (!IsClosed() && (handle_->fd() != STDOUT_FILENO) && ++ (handle_->fd() != STDERR_FILENO)) { ++ Close(); ++ } ++ delete handle_; ++} ++ ++void File::Close() { ++ ASSERT(handle_->fd() >= 0); ++ if (handle_->fd() == STDOUT_FILENO) { ++ // If stdout, redirect fd to /dev/null. ++ int null_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY)); ++ ASSERT(null_fd >= 0); ++ VOID_TEMP_FAILURE_RETRY(dup2(null_fd, handle_->fd())); ++ close(null_fd); ++ } else { ++ int err = close(handle_->fd()); ++ if (err != 0) { ++ const int kBufferSize = 1024; ++ char error_buf[kBufferSize]; ++ Syslog::PrintErr("%s\n", Utils::StrError(errno, error_buf, kBufferSize)); ++ } ++ } ++ handle_->set_fd(kClosedFd); ++} ++ ++intptr_t File::GetFD() { ++ return handle_->fd(); ++} ++ ++bool File::IsClosed() { ++ return handle_->fd() == kClosedFd; ++} ++ ++MappedMemory* File::Map(MapType type, ++ int64_t position, ++ int64_t length, ++ void* start) { ++ ASSERT(handle_->fd() >= 0); ++ ASSERT(length > 0); ++ void* hint = nullptr; ++ int prot = PROT_NONE; ++ int flags = MAP_PRIVATE; ++ switch (type) { ++ case kReadOnly: ++ prot = PROT_READ; ++ break; ++ case kReadExecute: ++ // Try to allocate near the VM's binary. ++ hint = reinterpret_cast(&Dart_Initialize); ++ prot = PROT_READ | PROT_EXEC; ++ break; ++ case kReadWrite: ++ prot = PROT_READ | PROT_WRITE; ++ break; ++ } ++ if (start != nullptr) { ++ hint = start; ++ flags |= MAP_FIXED; ++ } ++ void* addr = mmap(hint, length, prot, flags, handle_->fd(), position); ++ if (addr == MAP_FAILED) { ++ return NULL; ++ } ++ return new MappedMemory(addr, length, /*should_unmap=*/start == nullptr); ++} ++ ++void MappedMemory::Unmap() { ++ int result = munmap(address_, size_); ++ ASSERT(result == 0); ++ address_ = 0; ++ size_ = 0; ++} ++ ++int64_t File::Read(void* buffer, int64_t num_bytes) { ++ ASSERT(handle_->fd() >= 0); ++ return TEMP_FAILURE_RETRY(read(handle_->fd(), buffer, num_bytes)); ++} ++ ++int64_t File::Write(const void* buffer, int64_t num_bytes) { ++ ASSERT(handle_->fd() >= 0); ++ return TEMP_FAILURE_RETRY(write(handle_->fd(), buffer, num_bytes)); ++} ++ ++bool File::VPrint(const char* format, va_list args) { ++ // Measure. ++ va_list measure_args; ++ va_copy(measure_args, args); ++ intptr_t len = vsnprintf(NULL, 0, format, measure_args); ++ va_end(measure_args); ++ ++ char* buffer = reinterpret_cast(malloc(len + 1)); ++ ++ // Print. ++ va_list print_args; ++ va_copy(print_args, args); ++ vsnprintf(buffer, len + 1, format, print_args); ++ va_end(print_args); ++ ++ bool result = WriteFully(buffer, len); ++ free(buffer); ++ return result; ++} ++ ++int64_t File::Position() { ++ ASSERT(handle_->fd() >= 0); ++ return NO_RETRY_EXPECTED(lseek64(handle_->fd(), 0, SEEK_CUR)); ++} ++ ++bool File::SetPosition(int64_t position) { ++ ASSERT(handle_->fd() >= 0); ++ return NO_RETRY_EXPECTED(lseek64(handle_->fd(), position, SEEK_SET)) >= 0; ++} ++ ++bool File::Truncate(int64_t length) { ++ ASSERT(handle_->fd() >= 0); ++ return TEMP_FAILURE_RETRY(ftruncate64(handle_->fd(), length) != -1); ++} ++ ++bool File::Flush() { ++ ASSERT(handle_->fd() >= 0); ++ return NO_RETRY_EXPECTED(fsync(handle_->fd())) != -1; ++} ++ ++bool File::Lock(File::LockType lock, int64_t start, int64_t end) { ++ ASSERT(handle_->fd() >= 0); ++ ASSERT((end == -1) || (end > start)); ++ struct flock fl; ++ switch (lock) { ++ case File::kLockUnlock: ++ fl.l_type = F_UNLCK; ++ break; ++ case File::kLockShared: ++ case File::kLockBlockingShared: ++ fl.l_type = F_RDLCK; ++ break; ++ case File::kLockExclusive: ++ case File::kLockBlockingExclusive: ++ fl.l_type = F_WRLCK; ++ break; ++ default: ++ return false; ++ } ++ fl.l_whence = SEEK_SET; ++ fl.l_start = start; ++ fl.l_len = end == -1 ? 0 : end - start; ++ int cmd = F_SETLK; ++ if ((lock == File::kLockBlockingShared) || ++ (lock == File::kLockBlockingExclusive)) { ++ cmd = F_SETLKW; ++ } ++ return TEMP_FAILURE_RETRY(fcntl(handle_->fd(), cmd, &fl)) != -1; ++} ++ ++int64_t File::Length() { ++ ASSERT(handle_->fd() >= 0); ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstat64(handle_->fd(), &st)) == 0) { ++ return st.st_size; ++ } ++ return -1; ++} ++ ++File* File::FileOpenW(const wchar_t* system_name, FileOpenMode mode) { ++ UNREACHABLE(); ++ return NULL; ++} ++ ++File* File::OpenFD(int fd) { ++ return new File(new FileHandle(fd)); ++} ++ ++File* File::Open(Namespace* namespc, const char* name, FileOpenMode mode) { ++ NamespaceScope ns(namespc, name); ++ // Report errors for non-regular files. ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) { ++ // Only accept regular files, character devices, and pipes. ++ if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode) && !S_ISFIFO(st.st_mode)) { ++ errno = (S_ISDIR(st.st_mode)) ? EISDIR : ENOENT; ++ return NULL; ++ } ++ } ++ int flags = O_RDONLY; ++ if ((mode & kWrite) != 0) { ++ ASSERT((mode & kWriteOnly) == 0); ++ flags = (O_RDWR | O_CREAT); ++ } ++ if ((mode & kWriteOnly) != 0) { ++ ASSERT((mode & kWrite) == 0); ++ flags = (O_WRONLY | O_CREAT); ++ } ++ if ((mode & kTruncate) != 0) { ++ flags = flags | O_TRUNC; ++ } ++ flags |= O_CLOEXEC; ++ const int fd = TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), flags, 0666)); ++ if (fd < 0) { ++ return NULL; ++ } ++ if ((((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) || ++ (((mode & kWriteOnly) != 0) && ((mode & kTruncate) == 0))) { ++ int64_t position = NO_RETRY_EXPECTED(lseek64(fd, 0, SEEK_END)); ++ if (position < 0) { ++ return NULL; ++ } ++ } ++ return OpenFD(fd); ++} ++ ++Utils::CStringUniquePtr File::UriToPath(const char* uri) { ++ const char* path = (strlen(uri) >= 8 && strncmp(uri, "file:///", 8) == 0) ++ ? uri + 7 : uri; ++ UriDecoder uri_decoder(path); ++ if (uri_decoder.decoded() == nullptr) { ++ errno = EINVAL; ++ return Utils::CreateCStringUniquePtr(nullptr); ++ } ++ return Utils::CreateCStringUniquePtr(strdup(uri_decoder.decoded())); ++} ++ ++File* File::OpenUri(Namespace* namespc, const char* uri, FileOpenMode mode) { ++ auto path = UriToPath(uri); ++ if (path == nullptr) { ++ return nullptr; ++ } ++ return File::Open(namespc, path.get(), mode); ++} ++ ++File* File::OpenStdio(int fd) { ++ return new File(new FileHandle(fd)); ++} ++ ++bool File::Exists(Namespace* namespc, const char* name) { ++ NamespaceScope ns(namespc, name); ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) { ++ // Everything but a directory and a link is a file to Dart. ++ return !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode); ++ } else { ++ return false; ++ } ++} ++ ++bool File::ExistsUri(Namespace* namespc, const char* uri) { ++ auto path = UriToPath(uri); ++ if (path == nullptr) { ++ return false; ++ } ++ return File::Exists(namespc, path.get()); ++} ++ ++bool File::Create(Namespace* namespc, const char* name, bool exclusive) { ++ NamespaceScope ns(namespc, name); ++ int flags = O_RDONLY | O_CREAT | O_CLOEXEC; ++ if (exclusive) { ++ flags |= O_EXCL; ++ } ++ const int fd = TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), flags, 0666)); ++ if (fd < 0) { ++ return false; ++ } ++ // File.create returns a File, so we shouldn't be giving the illusion that the ++ // call has created a file or that a file already exists if there is already ++ // an entity at the same path that is a directory or a link. ++ bool is_file = true; ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstat64(fd, &st)) == 0) { ++ if (S_ISDIR(st.st_mode)) { ++ errno = EISDIR; ++ is_file = false; ++ } else if (S_ISLNK(st.st_mode)) { ++ errno = ENOENT; ++ is_file = false; ++ } ++ } ++ FDUtils::SaveErrorAndClose(fd); ++ return is_file; ++} ++ ++bool File::CreateLink(Namespace* namespc, ++ const char* name, ++ const char* target) { ++ NamespaceScope ns(namespc, name); ++ return NO_RETRY_EXPECTED(symlinkat(target, ns.fd(), ns.path())) == 0; ++} ++ ++bool File::CreatePipe(Namespace* namespc, File** readPipe, File** writePipe) { ++ int pipe_fds[2]; ++ int status = NO_RETRY_EXPECTED(pipe(pipe_fds)); ++ if (status != 0) { ++ return false; ++ } ++ *readPipe = OpenFD(pipe_fds[0]); ++ *writePipe = OpenFD(pipe_fds[1]); ++ return true; ++} ++ ++File::Type File::GetType(Namespace* namespc, ++ const char* name, ++ bool follow_links) { ++ NamespaceScope ns(namespc, name); ++ struct stat64 entry_info; ++ int stat_success; ++ if (follow_links) { ++ stat_success = ++ TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0)); ++ } else { ++ stat_success = TEMP_FAILURE_RETRY( ++ fstatat64(ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW)); ++ } ++ if (stat_success == -1) { ++ return File::kDoesNotExist; ++ } ++ if (S_ISDIR(entry_info.st_mode)) { ++ return File::kIsDirectory; ++ } ++ if (S_ISREG(entry_info.st_mode)) { ++ return File::kIsFile; ++ } ++ if (S_ISLNK(entry_info.st_mode)) { ++ return File::kIsLink; ++ } ++ if (S_ISSOCK(entry_info.st_mode)) { ++ return File::kIsSock; ++ } ++ if (S_ISFIFO(entry_info.st_mode)) { ++ return File::kIsPipe; ++ } ++ return File::kDoesNotExist; ++} ++ ++static void SetErrno(File::Type type) { ++ switch (type) { ++ case File::kIsDirectory: ++ errno = EISDIR; ++ break; ++ case File::kDoesNotExist: ++ errno = ENOENT; ++ break; ++ default: ++ errno = EINVAL; ++ break; ++ } ++} ++ ++static bool CheckTypeAndSetErrno(Namespace* namespc, ++ const char* name, ++ File::Type expected, ++ bool follow_links) { ++ File::Type actual = File::GetType(namespc, name, follow_links); ++ if (actual == expected) { ++ return true; ++ } ++ SetErrno(actual); ++ return false; ++} ++ ++bool File::Delete(Namespace* namespc, const char* name) { ++ File::Type type = File::GetType(namespc, name, true); ++ if (type == kIsFile || type == kIsSock || type == kIsPipe) { ++ NamespaceScope ns(namespc, name); ++ return (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0); ++ } ++ SetErrno(type); ++ return false; ++} ++ ++bool File::DeleteLink(Namespace* namespc, const char* name) { ++ NamespaceScope ns(namespc, name); ++ return CheckTypeAndSetErrno(namespc, name, kIsLink, false) && ++ (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0); ++} ++ ++bool File::Rename(Namespace* namespc, ++ const char* old_path, ++ const char* new_path) { ++ File::Type type = File::GetType(namespc, old_path, true); ++ if (type == kIsFile || type == kIsSock || type == kIsPipe) { ++ NamespaceScope oldns(namespc, old_path); ++ NamespaceScope newns(namespc, new_path); ++ return (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(), ++ newns.path())) == 0); ++ } ++ SetErrno(type); ++ return false; ++} ++ ++bool File::RenameLink(Namespace* namespc, ++ const char* old_path, ++ const char* new_path) { ++ NamespaceScope oldns(namespc, old_path); ++ NamespaceScope newns(namespc, new_path); ++ return CheckTypeAndSetErrno(namespc, old_path, kIsLink, false) && ++ (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(), ++ newns.path())) == 0); ++} ++ ++bool File::Copy(Namespace* namespc, ++ const char* old_path, ++ const char* new_path) { ++ File::Type type = File::GetType(namespc, old_path, true); ++ if (type != kIsFile && type != kIsSock && type != kIsPipe) { ++ SetErrno(type); ++ return false; ++ } ++ NamespaceScope oldns(namespc, old_path); ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstatat64(oldns.fd(), oldns.path(), &st, 0)) != 0) { ++ return false; ++ } ++ const int old_fd = TEMP_FAILURE_RETRY( ++ openat64(oldns.fd(), oldns.path(), O_RDONLY | O_CLOEXEC)); ++ if (old_fd < 0) { ++ return false; ++ } ++ NamespaceScope newns(namespc, new_path); ++ const int new_fd = TEMP_FAILURE_RETRY( ++ openat64(newns.fd(), newns.path(), ++ O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, st.st_mode)); ++ if (new_fd < 0) { ++ close(old_fd); ++ return false; ++ } ++ int64_t offset = 0; ++ intptr_t result = 1; ++ while (result > 0) { ++ // Loop to ensure we copy everything, and not only up to 2GB. ++ result = NO_RETRY_EXPECTED(sendfile64(new_fd, old_fd, &offset, kMaxUint32)); ++ } ++ // From sendfile man pages: ++ // Applications may wish to fall back to read(2)/write(2) in the case ++ // where sendfile() fails with EINVAL or ENOSYS. ++ if ((result < 0) && ((errno == EINVAL) || (errno == ENOSYS))) { ++ const intptr_t kBufferSize = 8 * KB; ++ uint8_t* buffer = reinterpret_cast(malloc(kBufferSize)); ++ while ((result = TEMP_FAILURE_RETRY(read(old_fd, buffer, kBufferSize))) > ++ 0) { ++ int wrote = TEMP_FAILURE_RETRY(write(new_fd, buffer, result)); ++ if (wrote != result) { ++ result = -1; ++ break; ++ } ++ } ++ free(buffer); ++ } ++ int e = errno; ++ close(old_fd); ++ close(new_fd); ++ if (result < 0) { ++ VOID_NO_RETRY_EXPECTED(unlinkat(newns.fd(), newns.path(), 0)); ++ errno = e; ++ return false; ++ } ++ return true; ++} ++ ++static bool StatHelper(Namespace* namespc, ++ const char* name, ++ struct stat64* st) { ++ NamespaceScope ns(namespc, name); ++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), st, 0)) != 0) { ++ return false; ++ } ++ // Signal an error if it's a directory. ++ if (S_ISDIR(st->st_mode)) { ++ errno = EISDIR; ++ return false; ++ } ++ // Otherwise assume the caller knows what it's doing. ++ return true; ++} ++ ++int64_t File::LengthFromPath(Namespace* namespc, const char* name) { ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return -1; ++ } ++ return st.st_size; ++} ++ ++static int64_t TimespecToMilliseconds(const struct timespec& t) { ++ return static_cast(t.tv_sec) * 1000L + ++ static_cast(t.tv_nsec) / 1000000L; ++} ++ ++static void MillisecondsToTimespec(int64_t millis, struct timespec* t) { ++ ASSERT(t != NULL); ++ t->tv_sec = millis / kMillisecondsPerSecond; ++ t->tv_nsec = (millis % kMillisecondsPerSecond) * 1000L; ++} ++ ++void File::Stat(Namespace* namespc, const char* name, int64_t* data) { ++ NamespaceScope ns(namespc, name); ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) { ++ if (S_ISREG(st.st_mode)) { ++ data[kType] = kIsFile; ++ } else if (S_ISDIR(st.st_mode)) { ++ data[kType] = kIsDirectory; ++ } else if (S_ISLNK(st.st_mode)) { ++ data[kType] = kIsLink; ++ } else if (S_ISSOCK(st.st_mode)) { ++ data[kType] = kIsSock; ++ } else if (S_ISFIFO(st.st_mode)) { ++ data[kType] = kIsPipe; ++ } else { ++ data[kType] = kDoesNotExist; ++ } ++ data[kCreatedTime] = TimespecToMilliseconds(st.st_ctim); ++ data[kModifiedTime] = TimespecToMilliseconds(st.st_mtim); ++ data[kAccessedTime] = TimespecToMilliseconds(st.st_atim); ++ data[kMode] = st.st_mode; ++ data[kSize] = st.st_size; ++ } else { ++ data[kType] = kDoesNotExist; ++ } ++} ++ ++time_t File::LastModified(Namespace* namespc, const char* name) { ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return -1; ++ } ++ return st.st_mtime; ++} ++ ++time_t File::LastAccessed(Namespace* namespc, const char* name) { ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return -1; ++ } ++ return st.st_atime; ++} ++ ++bool File::SetLastAccessed(Namespace* namespc, ++ const char* name, ++ int64_t millis) { ++ // First get the current times. ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return false; ++ } ++ ++ // Set the new time: ++ NamespaceScope ns(namespc, name); ++ struct timespec times[2]; ++ MillisecondsToTimespec(millis, ×[0]); ++ times[1] = st.st_mtim; ++ return utimensat(ns.fd(), ns.path(), times, 0) == 0; ++} ++ ++bool File::SetLastModified(Namespace* namespc, ++ const char* name, ++ int64_t millis) { ++ // First get the current times. ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return false; ++ } ++ ++ // Set the new time: ++ NamespaceScope ns(namespc, name); ++ struct timespec times[2]; ++ times[0] = st.st_atim; ++ MillisecondsToTimespec(millis, ×[1]); ++ return utimensat(ns.fd(), ns.path(), times, 0) == 0; ++} ++ ++const char* File::LinkTarget(Namespace* namespc, ++ const char* name, ++ char* dest, ++ int dest_size) { ++ NamespaceScope ns(namespc, name); ++ struct stat64 link_stats; ++ const int status = TEMP_FAILURE_RETRY( ++ fstatat64(ns.fd(), ns.path(), &link_stats, AT_SYMLINK_NOFOLLOW)); ++ if (status != 0) { ++ return NULL; ++ } ++ if (!S_ISLNK(link_stats.st_mode)) { ++ errno = ENOENT; ++ return NULL; ++ } ++ // Don't rely on the link_stats.st_size for the size of the link ++ // target. For some filesystems, e.g. procfs, this value is always ++ // 0. Also the link might have changed before the readlink call. ++ const int kBufferSize = PATH_MAX + 1; ++ char target[kBufferSize]; ++ const int target_size = ++ TEMP_FAILURE_RETRY(readlinkat(ns.fd(), ns.path(), target, kBufferSize)); ++ if (target_size <= 0) { ++ return NULL; ++ } ++ if (dest == NULL) { ++ dest = DartUtils::ScopedCString(target_size + 1); ++ } else { ++ ASSERT(dest_size > 0); ++ if (dest_size <= target_size) { ++ return NULL; ++ } ++ } ++ memmove(dest, target, target_size); ++ dest[target_size] = '\0'; ++ return dest; ++} ++ ++bool File::IsAbsolutePath(const char* pathname) { ++ return (pathname != NULL) && (pathname[0] == '/'); ++} ++ ++intptr_t File::ReadLinkInto(const char* pathname, ++ char* result, ++ size_t result_size) { ++ ASSERT(pathname != NULL); ++ ASSERT(IsAbsolutePath(pathname)); ++ struct stat64 link_stats; ++ if (TEMP_FAILURE_RETRY(lstat64(pathname, &link_stats)) != 0) { ++ return -1; ++ } ++ if (!S_ISLNK(link_stats.st_mode)) { ++ errno = ENOENT; ++ return -1; ++ } ++ size_t target_size = ++ TEMP_FAILURE_RETRY(readlink(pathname, result, result_size)); ++ if (target_size <= 0) { ++ return -1; ++ } ++ // readlink returns non-zero terminated strings. Append. ++ if (target_size < result_size) { ++ result[target_size] = '\0'; ++ target_size++; ++ } ++ return target_size; ++} ++ ++const char* File::ReadLink(const char* pathname) { ++ // Don't rely on the link_stats.st_size for the size of the link ++ // target. For some filesystems, e.g. procfs, this value is always ++ // 0. Also the link might have changed before the readlink call. ++ const int kBufferSize = PATH_MAX + 1; ++ char target[kBufferSize]; ++ size_t target_size = ReadLinkInto(pathname, target, kBufferSize); ++ if (target_size <= 0) { ++ return NULL; ++ } ++ char* target_name = DartUtils::ScopedCString(target_size); ++ ASSERT(target_name != NULL); ++ memmove(target_name, target, target_size); ++ return target_name; ++} ++ ++const char* File::GetCanonicalPath(Namespace* namespc, ++ const char* name, ++ char* dest, ++ int dest_size) { ++ if (name == NULL) { ++ return NULL; ++ } ++ if (!Namespace::IsDefault(namespc)) { ++ // TODO(zra): There is no realpathat(). Also chasing a symlink might result ++ // in a path to something outside of the namespace, so canonicalizing paths ++ // would have to be done carefully. For now, don't do anything. ++ return name; ++ } ++ char* abs_path; ++ if (dest == NULL) { ++ dest = DartUtils::ScopedCString(PATH_MAX + 1); ++ } else { ++ ASSERT(dest_size >= PATH_MAX); ++ } ++ ASSERT(dest != NULL); ++ do { ++ abs_path = realpath(name, dest); ++ } while ((abs_path == NULL) && (errno == EINTR)); ++ ASSERT(abs_path == NULL || IsAbsolutePath(abs_path)); ++ ASSERT(abs_path == NULL || (abs_path == dest)); ++ return abs_path; ++} ++ ++const char* File::PathSeparator() { ++ return "/"; ++} ++ ++const char* File::StringEscapedPathSeparator() { ++ return "/"; ++} ++ ++File::StdioHandleType File::GetStdioHandleType(int fd) { ++ struct stat64 buf; ++ int result = TEMP_FAILURE_RETRY(fstat64(fd, &buf)); ++ if (result == -1) { ++ return kTypeError; ++ } ++ if (S_ISCHR(buf.st_mode)) { ++ return kTerminal; ++ } ++ if (S_ISFIFO(buf.st_mode)) { ++ return kPipe; ++ } ++ if (S_ISSOCK(buf.st_mode)) { ++ return kSocket; ++ } ++ if (S_ISREG(buf.st_mode)) { ++ return kFile; ++ } ++ return kOther; ++} ++ ++File::Identical File::AreIdentical(Namespace* namespc_1, ++ const char* file_1, ++ Namespace* namespc_2, ++ const char* file_2) { ++ struct stat64 file_1_info; ++ struct stat64 file_2_info; ++ int status; ++ { ++ NamespaceScope ns1(namespc_1, file_1); ++ status = TEMP_FAILURE_RETRY( ++ fstatat64(ns1.fd(), ns1.path(), &file_1_info, AT_SYMLINK_NOFOLLOW)); ++ if (status == -1) { ++ return File::kError; ++ } ++ } ++ { ++ NamespaceScope ns2(namespc_2, file_2); ++ status = TEMP_FAILURE_RETRY( ++ fstatat64(ns2.fd(), ns2.path(), &file_2_info, AT_SYMLINK_NOFOLLOW)); ++ if (status == -1) { ++ return File::kError; ++ } ++ } ++ return ((file_1_info.st_ino == file_2_info.st_ino) && ++ (file_1_info.st_dev == file_2_info.st_dev)) ++ ? File::kIdentical ++ : File::kDifferent; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/file_system_watcher_ohos.cc b/runtime/bin/file_system_watcher_ohos.cc +new file mode 100644 +index 00000000000..fa030a251e8 +--- /dev/null ++++ b/runtime/bin/file_system_watcher_ohos.cc +@@ -0,0 +1,149 @@ ++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/file_system_watcher.h" ++ ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/fdutils.h" ++#include "bin/file.h" ++#include "bin/socket.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool FileSystemWatcher::IsSupported() { ++ return true; ++} ++ ++intptr_t FileSystemWatcher::Init() { ++ int id = NO_RETRY_EXPECTED(inotify_init1(IN_CLOEXEC)); ++ if (id < 0) { ++ return -1; ++ } ++ // Some systems dosn't support setting this as non-blocking. Since watching ++ // internals are kept away from the user, we know it's possible to continue, ++ // even if setting non-blocking fails. ++ FDUtils::SetNonBlocking(id); ++ return id; ++} ++ ++void FileSystemWatcher::Close(intptr_t id) { ++ USE(id); ++} ++ ++intptr_t FileSystemWatcher::WatchPath(intptr_t id, ++ Namespace* namespc, ++ const char* path, ++ int events, ++ bool recursive) { ++ int list_events = IN_DELETE_SELF | IN_MOVE_SELF; ++ if ((events & kCreate) != 0) { ++ list_events |= IN_CREATE; ++ } ++ if ((events & kModifyContent) != 0) { ++ list_events |= IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY; ++ } ++ if ((events & kDelete) != 0) { ++ list_events |= IN_DELETE; ++ } ++ if ((events & kMove) != 0) { ++ list_events |= IN_MOVE; ++ } ++ const char* resolved_path = File::GetCanonicalPath(namespc, path); ++ path = resolved_path != NULL ? resolved_path : path; ++ int path_id = NO_RETRY_EXPECTED(inotify_add_watch(id, path, list_events)); ++ if (path_id < 0) { ++ return -1; ++ } ++ return path_id; ++} ++ ++void FileSystemWatcher::UnwatchPath(intptr_t id, intptr_t path_id) { ++ VOID_NO_RETRY_EXPECTED(inotify_rm_watch(id, path_id)); ++} ++ ++intptr_t FileSystemWatcher::GetSocketId(intptr_t id, intptr_t path_id) { ++ USE(path_id); ++ return id; ++} ++ ++static int InotifyEventToMask(struct inotify_event* e) { ++ int mask = 0; ++ if ((e->mask & IN_CLOSE_WRITE) != 0 || (e->mask & IN_MODIFY) != 0) { ++ mask |= FileSystemWatcher::kModifyContent; ++ } ++ if ((e->mask & IN_ATTRIB) != 0) { ++ mask |= FileSystemWatcher::kModifyAttribute; ++ } ++ if ((e->mask & IN_CREATE) != 0) { ++ mask |= FileSystemWatcher::kCreate; ++ } ++ if ((e->mask & IN_MOVE) != 0) { ++ mask |= FileSystemWatcher::kMove; ++ } ++ if ((e->mask & IN_DELETE) != 0) { ++ mask |= FileSystemWatcher::kDelete; ++ } ++ if ((e->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) != 0) { ++ mask |= FileSystemWatcher::kDeleteSelf; ++ } ++ if ((e->mask & IN_ISDIR) != 0) { ++ mask |= FileSystemWatcher::kIsDir; ++ } ++ return mask; ++} ++ ++Dart_Handle FileSystemWatcher::ReadEvents(intptr_t id, intptr_t path_id) { ++ USE(path_id); ++ const intptr_t kEventSize = sizeof(struct inotify_event); ++ const intptr_t kBufferSize = kEventSize + NAME_MAX + 1; ++ uint8_t buffer[kBufferSize]; ++ intptr_t bytes = ++ SocketBase::Read(id, buffer, kBufferSize, SocketBase::kAsync); ++ if (bytes < 0) { ++ return DartUtils::NewDartOSError(); ++ } ++ const intptr_t kMaxCount = bytes / kEventSize; ++ Dart_Handle events = Dart_NewList(kMaxCount); ++ intptr_t offset = 0; ++ intptr_t i = 0; ++ while (offset < bytes) { ++ struct inotify_event* e = ++ reinterpret_cast(buffer + offset); ++ if ((e->mask & IN_IGNORED) == 0) { ++ Dart_Handle event = Dart_NewList(5); ++ int mask = InotifyEventToMask(e); ++ Dart_ListSetAt(event, 0, Dart_NewInteger(mask)); ++ Dart_ListSetAt(event, 1, Dart_NewInteger(e->cookie)); ++ if (e->len > 0) { ++ Dart_Handle name = Dart_NewStringFromUTF8( ++ reinterpret_cast(e->name), strlen(e->name)); ++ if (Dart_IsError(name)) { ++ return name; ++ } ++ Dart_ListSetAt(event, 2, name); ++ } else { ++ Dart_ListSetAt(event, 2, Dart_Null()); ++ } ++ Dart_ListSetAt(event, 3, Dart_NewBoolean((e->mask & IN_MOVED_TO) != 0u)); ++ Dart_ListSetAt(event, 4, Dart_NewInteger(e->wd)); ++ Dart_ListSetAt(events, i, event); ++ i++; ++ } ++ offset += kEventSize + e->len; ++ } ++ ASSERT(offset == bytes); ++ return events; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/io_impl_sources.gni b/runtime/bin/io_impl_sources.gni +index f2c1be724ef..0a5d97d1bbe 100644 +--- a/runtime/bin/io_impl_sources.gni ++++ b/runtime/bin/io_impl_sources.gni +@@ -16,6 +16,8 @@ io_impl_sources = [ + "eventhandler_fuchsia.h", + "eventhandler_linux.cc", + "eventhandler_linux.h", ++ "eventhandler_ohos.cc", ++ "eventhandler_ohos.h", + "eventhandler_macos.cc", + "eventhandler_macos.h", + "eventhandler_win.cc", +@@ -25,6 +27,7 @@ io_impl_sources = [ + "file_system_watcher_android.cc", + "file_system_watcher_fuchsia.cc", + "file_system_watcher_linux.cc", ++ "file_system_watcher_ohos.cc", + "file_system_watcher_macos.cc", + "file_system_watcher_win.cc", + "filter.cc", +@@ -41,6 +44,7 @@ io_impl_sources = [ + "namespace_fuchsia.cc", + "namespace_fuchsia.h", + "namespace_linux.cc", ++ "namespace_ohos.cc", + "namespace_macos.cc", + "namespace_win.cc", + "platform.cc", +@@ -48,6 +52,7 @@ io_impl_sources = [ + "platform_android.cc", + "platform_fuchsia.cc", + "platform_linux.cc", ++ "platform_ohos.cc", + "platform_macos.cc", + "platform_macos.h", + "platform_win.cc", +@@ -56,6 +61,7 @@ io_impl_sources = [ + "process_android.cc", + "process_fuchsia.cc", + "process_linux.cc", ++ "process_ohos.cc", + "process_macos.cc", + "process_win.cc", + "reference_counting.h", +@@ -70,6 +76,7 @@ io_impl_sources = [ + "security_context_android.cc", + "security_context_fuchsia.cc", + "security_context_linux.cc", ++ "security_context_ohos.cc", + "security_context_macos.cc", + "security_context_win.cc", + "socket.cc", +@@ -83,6 +90,8 @@ io_impl_sources = [ + "socket_base_fuchsia.h", + "socket_base_linux.cc", + "socket_base_linux.h", ++ "socket_base_ohos.cc", ++ "socket_base_ohos.h", + "socket_base_macos.cc", + "socket_base_macos.h", + "socket_base_posix.cc", +@@ -90,6 +99,7 @@ io_impl_sources = [ + "socket_base_win.h", + "socket_fuchsia.cc", + "socket_linux.cc", ++ "socket_ohos.cc", + "socket_macos.cc", + "socket_win.cc", + "stdio.cc", +@@ -97,6 +107,7 @@ io_impl_sources = [ + "stdio_android.cc", + "stdio_fuchsia.cc", + "stdio_linux.cc", ++ "stdio_ohos.cc", + "stdio_macos.cc", + "stdio_win.cc", + "sync_socket.cc", +@@ -104,6 +115,7 @@ io_impl_sources = [ + "sync_socket_android.cc", + "sync_socket_fuchsia.cc", + "sync_socket_linux.cc", ++ "sync_socket_ohos.cc", + "sync_socket_macos.cc", + "sync_socket_win.cc", + "typed_data_utils.cc", +diff --git a/runtime/bin/namespace_ohos.cc b/runtime/bin/namespace_ohos.cc +new file mode 100644 +index 00000000000..71b68ca618d +--- /dev/null ++++ b/runtime/bin/namespace_ohos.cc +@@ -0,0 +1,159 @@ ++// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/namespace.h" ++ ++#include ++#include ++ ++#include "bin/file.h" ++#include "platform/signal_blocker.h" ++#include "platform/text_buffer.h" ++ ++namespace dart { ++namespace bin { ++ ++class NamespaceImpl { ++ public: ++ explicit NamespaceImpl(intptr_t rootfd) : rootfd_(rootfd), cwd_(strdup("/")) { ++ ASSERT(rootfd_ > 0); ++ cwdfd_ = dup(rootfd_); ++ ASSERT(cwdfd_ > 0); ++ } ++ ++ explicit NamespaceImpl(const char* path) ++ : rootfd_(TEMP_FAILURE_RETRY(open64(path, O_DIRECTORY))), ++ cwd_(strdup("/")) { ++ ASSERT(rootfd_ > 0); ++ cwdfd_ = dup(rootfd_); ++ ASSERT(cwdfd_ > 0); ++ } ++ ++ ~NamespaceImpl() { ++ NO_RETRY_EXPECTED(close(rootfd_)); ++ free(cwd_); ++ NO_RETRY_EXPECTED(close(cwdfd_)); ++ } ++ ++ intptr_t rootfd() const { return rootfd_; } ++ char* cwd() const { return cwd_; } ++ intptr_t cwdfd() const { return cwdfd_; } ++ ++ bool SetCwd(Namespace* namespc, const char* new_path) { ++ NamespaceScope ns(namespc, new_path); ++ const intptr_t new_cwdfd = ++ TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_DIRECTORY)); ++ if (new_cwdfd < 0) { ++ return false; ++ } ++ ++ TextBuffer tbuf(PATH_MAX); ++ if (!File::IsAbsolutePath(new_path)) { ++ tbuf.AddString(cwd_); ++ } ++ tbuf.AddString(File::PathSeparator()); ++ tbuf.AddString(ns.path()); ++ ++ // Normalize it. ++ char result[PATH_MAX]; ++ const intptr_t result_len = ++ File::CleanUnixPath(tbuf.buffer(), result, PATH_MAX); ++ if (result_len < 0) { ++ errno = ENAMETOOLONG; ++ return false; ++ } ++ ++ free(cwd_); ++ cwd_ = strdup(result); ++ close(cwdfd_); ++ cwdfd_ = new_cwdfd; ++ return true; ++ } ++ ++ private: ++ intptr_t rootfd_; // dirfd for the namespace root. ++ char* cwd_; // cwd relative to the namespace. ++ intptr_t cwdfd_; // dirfd for the cwd. ++ ++ DISALLOW_COPY_AND_ASSIGN(NamespaceImpl); ++}; ++ ++Namespace* Namespace::Create(intptr_t namespc) { ++ NamespaceImpl* namespc_impl = NULL; ++ if (namespc != kNone) { ++ namespc_impl = new NamespaceImpl(namespc); ++ } ++ return new Namespace(namespc_impl); ++} ++ ++Namespace* Namespace::Create(const char* path) { ++ return new Namespace(new NamespaceImpl(path)); ++} ++ ++Namespace::~Namespace() { ++ delete namespc_; ++} ++ ++intptr_t Namespace::Default() { ++ return kNone; ++} ++ ++const char* Namespace::GetCurrent(Namespace* namespc) { ++ if (Namespace::IsDefault(namespc)) { ++ char buffer[PATH_MAX]; ++ if (getcwd(buffer, PATH_MAX) == NULL) { ++ return NULL; ++ } ++ return DartUtils::ScopedCopyCString(buffer); ++ } ++ const char* cwd = namespc->namespc()->cwd(); ++ return cwd; ++} ++ ++bool Namespace::SetCurrent(Namespace* namespc, const char* path) { ++ if (Namespace::IsDefault(namespc)) { ++ return (NO_RETRY_EXPECTED(chdir(path)) == 0); ++ } ++ return namespc->namespc()->SetCwd(namespc, path); ++} ++ ++void Namespace::ResolvePath(Namespace* namespc, ++ const char* path, ++ intptr_t* dirfd, ++ const char** resolved_path) { ++ ASSERT(dirfd != NULL); ++ ASSERT(resolved_path != NULL); ++ if (Namespace::IsDefault(namespc)) { ++ *dirfd = AT_FDCWD; ++ *resolved_path = path; ++ return; ++ } ++ if (File::IsAbsolutePath(path)) { ++ *dirfd = namespc->namespc()->rootfd(); ++ if (strcmp(path, File::PathSeparator()) == 0) { ++ // Change "/" to ".". ++ *resolved_path = "."; ++ } else { ++ // Otherwise strip off the leading "/". ++ *resolved_path = &path[1]; ++ } ++ } else { ++ *dirfd = namespc->namespc()->cwdfd(); ++ *resolved_path = path; ++ } ++} ++ ++NamespaceScope::NamespaceScope(Namespace* namespc, const char* path) { ++ Namespace::ResolvePath(namespc, path, &fd_, &path_); ++} ++ ++NamespaceScope::~NamespaceScope() {} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/platform_ohos.cc b/runtime/bin/platform_ohos.cc +new file mode 100644 +index 00000000000..2ff45e4236d +--- /dev/null ++++ b/runtime/bin/platform_ohos.cc +@@ -0,0 +1,183 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/platform.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "bin/console.h" ++#include "bin/file.h" ++ ++namespace dart { ++namespace bin { ++ ++const char* Platform::executable_name_ = NULL; ++int Platform::script_index_ = 1; ++char** Platform::argv_ = NULL; ++ ++static void segv_handler(int signal, siginfo_t* siginfo, void* context) { ++ Syslog::PrintErr( ++ "\n===== CRASH =====\n" ++ "si_signo=%s(%d), si_code=%d, si_addr=%p\n", ++ strsignal(siginfo->si_signo), siginfo->si_signo, siginfo->si_code, ++ siginfo->si_addr); ++ Dart_DumpNativeStackTrace(context); ++ Dart_PrepareToAbort(); ++ abort(); ++} ++ ++bool Platform::Initialize() { ++ // Turn off the signal handler for SIGPIPE as it causes the process ++ // to terminate on writing to a closed pipe. Without the signal ++ // handler error EPIPE is set instead. ++ struct sigaction act = {}; ++ act.sa_handler = SIG_IGN; ++ if (sigaction(SIGPIPE, &act, 0) != 0) { ++ perror("Setting signal handler failed"); ++ return false; ++ } ++ ++ // tcsetattr raises SIGTTOU if we try to set console attributes when ++ // backgrounded, which suspends the process. Ignoring the signal prevents ++ // us from being suspended and lets us fail gracefully instead. ++ sigset_t signal_mask; ++ sigemptyset(&signal_mask); ++ sigaddset(&signal_mask, SIGTTOU); ++ if (sigprocmask(SIG_BLOCK, &signal_mask, NULL) < 0) { ++ perror("Setting signal handler failed"); ++ return false; ++ } ++ ++ act.sa_flags = SA_SIGINFO; ++ act.sa_sigaction = &segv_handler; ++ if (sigemptyset(&act.sa_mask) != 0) { ++ perror("sigemptyset() failed."); ++ return false; ++ } ++ if (sigaddset(&act.sa_mask, SIGPROF) != 0) { ++ perror("sigaddset() failed"); ++ return false; ++ } ++ if (sigaction(SIGSEGV, &act, NULL) != 0) { ++ perror("sigaction() failed."); ++ return false; ++ } ++ if (sigaction(SIGBUS, &act, NULL) != 0) { ++ perror("sigaction() failed."); ++ return false; ++ } ++ if (sigaction(SIGTRAP, &act, NULL) != 0) { ++ perror("sigaction() failed."); ++ return false; ++ } ++ if (sigaction(SIGILL, &act, NULL) != 0) { ++ perror("sigaction() failed."); ++ return false; ++ } ++ return true; ++} ++ ++int Platform::NumberOfProcessors() { ++ return sysconf(_SC_NPROCESSORS_ONLN); ++} ++ ++const char* Platform::OperatingSystemVersion() { ++ struct utsname info; ++ int ret = uname(&info); ++ if (ret != 0) { ++ return NULL; ++ } ++ const char* kFormat = "%s %s %s"; ++ int len = ++ snprintf(NULL, 0, kFormat, info.sysname, info.release, info.version); ++ if (len <= 0) { ++ return NULL; ++ } ++ char* result = DartUtils::ScopedCString(len + 1); ++ ASSERT(result != NULL); ++ len = snprintf(result, len + 1, kFormat, info.sysname, info.release, ++ info.version); ++ if (len <= 0) { ++ return NULL; ++ } ++ return result; ++} ++ ++const char* Platform::LibraryPrefix() { ++ return "lib"; ++} ++ ++const char* Platform::LibraryExtension() { ++ return "so"; ++} ++ ++const char* Platform::LocaleName() { ++ char* lang = getenv("LANG"); ++ if (lang == NULL) { ++ return "en_US"; ++ } ++ return lang; ++} ++ ++bool Platform::LocalHostname(char* buffer, intptr_t buffer_length) { ++ return gethostname(buffer, buffer_length) == 0; ++} ++ ++char** Platform::Environment(intptr_t* count) { ++ // Using environ directly is only safe as long as we do not ++ // provide access to modifying environment variables. ++ intptr_t i = 0; ++ char** tmp = environ; ++ while (*(tmp++) != NULL) { ++ i++; ++ } ++ *count = i; ++ char** result; ++ result = reinterpret_cast(Dart_ScopeAllocate(i * sizeof(*result))); ++ for (intptr_t current = 0; current < i; current++) { ++ result[current] = environ[current]; ++ } ++ return result; ++} ++ ++const char* Platform::GetExecutableName() { ++ return executable_name_; ++} ++ ++const char* Platform::ResolveExecutablePath() { ++ return File::ReadLink("/proc/self/exe"); ++} ++ ++intptr_t Platform::ResolveExecutablePathInto(char* result, size_t result_size) { ++ return File::ReadLinkInto("/proc/self/exe", result, result_size); ++} ++ ++void Platform::SetProcessName(const char* name) { ++ prctl(PR_SET_NAME, reinterpret_cast(name), 0, 0, 0); // NOLINT ++} ++ ++void Platform::Exit(int exit_code) { ++ Console::RestoreConfig(); ++ Dart_PrepareToAbort(); ++ exit(exit_code); ++} ++ ++void Platform::SetCoreDumpResourceLimit(int value) { ++ rlimit limit = {static_cast(value), static_cast(value)}; ++ setrlimit(RLIMIT_CORE, &limit); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/process_ohos.cc b/runtime/bin/process_ohos.cc +new file mode 100644 +index 00000000000..5cad067780f +--- /dev/null ++++ b/runtime/bin/process_ohos.cc +@@ -0,0 +1,1165 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/process.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/dartutils.h" ++#include "bin/directory.h" ++#include "bin/fdutils.h" ++#include "bin/file.h" ++#include "bin/lockers.h" ++#include "bin/reference_counting.h" ++#include "bin/thread.h" ++#include "platform/syslog.h" ++ ++#include "platform/signal_blocker.h" ++#include "platform/utils.h" ++ ++extern char** environ; ++ ++namespace dart { ++namespace bin { ++ ++int Process::global_exit_code_ = 0; ++Mutex* Process::global_exit_code_mutex_ = nullptr; ++Process::ExitHook Process::exit_hook_ = NULL; ++ ++// ProcessInfo is used to map a process id to the file descriptor for ++// the pipe used to communicate the exit code of the process to Dart. ++// ProcessInfo objects are kept in the static singly-linked ++// ProcessInfoList. ++class ProcessInfo { ++ public: ++ ProcessInfo(pid_t pid, intptr_t fd) : pid_(pid), fd_(fd) {} ++ ~ProcessInfo() { ++ int closed = close(fd_); ++ if (closed != 0) { ++ FATAL("Failed to close process exit code pipe"); ++ } ++ } ++ pid_t pid() { return pid_; } ++ intptr_t fd() { return fd_; } ++ ProcessInfo* next() { return next_; } ++ void set_next(ProcessInfo* info) { next_ = info; } ++ ++ private: ++ pid_t pid_; ++ intptr_t fd_; ++ ProcessInfo* next_; ++ ++ DISALLOW_COPY_AND_ASSIGN(ProcessInfo); ++}; ++ ++// Singly-linked list of ProcessInfo objects for all active processes ++// started from Dart. ++class ProcessInfoList { ++ public: ++ static void Init(); ++ static void Cleanup(); ++ ++ static void AddProcess(pid_t pid, intptr_t fd) { ++ MutexLocker locker(mutex_); ++ ProcessInfo* info = new ProcessInfo(pid, fd); ++ info->set_next(active_processes_); ++ active_processes_ = info; ++ } ++ ++ static intptr_t LookupProcessExitFd(pid_t pid) { ++ MutexLocker locker(mutex_); ++ ProcessInfo* current = active_processes_; ++ while (current != NULL) { ++ if (current->pid() == pid) { ++ return current->fd(); ++ } ++ current = current->next(); ++ } ++ return 0; ++ } ++ ++ static void RemoveProcess(pid_t pid) { ++ MutexLocker locker(mutex_); ++ ProcessInfo* prev = NULL; ++ ProcessInfo* current = active_processes_; ++ while (current != NULL) { ++ if (current->pid() == pid) { ++ if (prev == NULL) { ++ active_processes_ = current->next(); ++ } else { ++ prev->set_next(current->next()); ++ } ++ delete current; ++ return; ++ } ++ prev = current; ++ current = current->next(); ++ } ++ } ++ ++ private: ++ // Linked list of ProcessInfo objects for all active processes ++ // started from Dart code. ++ static ProcessInfo* active_processes_; ++ // Mutex protecting all accesses to the linked list of active ++ // processes. ++ static Mutex* mutex_; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessInfoList); ++}; ++ ++ProcessInfo* ProcessInfoList::active_processes_ = NULL; ++Mutex* ProcessInfoList::mutex_ = nullptr; ++ ++// The exit code handler sets up a separate thread which waits for child ++// processes to terminate. That separate thread can then get the exit code from ++// processes that have exited and communicate it to Dart through the ++// event loop. ++class ExitCodeHandler { ++ public: ++ static void Init(); ++ static void Cleanup(); ++ ++ // Notify the ExitCodeHandler that another process exists. ++ static void ProcessStarted() { ++ // Multiple isolates could be starting processes at the same ++ // time. Make sure that only one ExitCodeHandler thread exists. ++ MonitorLocker locker(monitor_); ++ process_count_++; ++ ++ monitor_->Notify(); ++ ++ if (running_) { ++ return; ++ } ++ ++ // Start thread that handles process exits when wait returns. ++ int result = ++ Thread::Start("dart:io Process.start", ExitCodeHandlerEntry, 0); ++ if (result != 0) { ++ FATAL1("Failed to start exit code handler worker thread %d", result); ++ } ++ ++ running_ = true; ++ } ++ ++ static void TerminateExitCodeThread() { ++ MonitorLocker locker(monitor_); ++ ++ if (!running_) { ++ return; ++ } ++ ++ // Set terminate_done_ to false, so we can use it as a guard for our ++ // monitor. ++ running_ = false; ++ ++ // Wake up the [ExitCodeHandler] thread which is blocked on `wait()` (see ++ // [ExitCodeHandlerEntry]). ++ if (TEMP_FAILURE_RETRY(fork()) == 0) { ++ // Avoid calling any atexit callbacks to prevent deadlocks. ++ _exit(0); ++ } ++ ++ monitor_->Notify(); ++ ++ while (!terminate_done_) { ++ monitor_->Wait(Monitor::kNoTimeout); ++ } ++ } ++ ++ private: ++ // Entry point for the separate exit code handler thread started by ++ // the ExitCodeHandler. ++ static void ExitCodeHandlerEntry(uword param) { ++ pid_t pid = 0; ++ int status = 0; ++ while (true) { ++ { ++ MonitorLocker locker(monitor_); ++ while (running_ && process_count_ == 0) { ++ monitor_->Wait(Monitor::kNoTimeout); ++ } ++ if (!running_) { ++ terminate_done_ = true; ++ monitor_->Notify(); ++ return; ++ } ++ } ++ ++ if ((pid = TEMP_FAILURE_RETRY(wait(&status))) > 0) { ++ int exit_code = 0; ++ int negative = 0; ++ if (WIFEXITED(status)) { ++ exit_code = WEXITSTATUS(status); ++ } ++ if (WIFSIGNALED(status)) { ++ exit_code = WTERMSIG(status); ++ negative = 1; ++ } ++ intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid); ++ if (exit_code_fd != 0) { ++ int message[2] = {exit_code, negative}; ++ ssize_t result = ++ FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message)); ++ // If the process has been closed, the read end of the exit ++ // pipe has been closed. It is therefore not a problem that ++ // write fails with a broken pipe error. Other errors should ++ // not happen. ++ if ((result != -1) && (result != sizeof(message))) { ++ FATAL("Failed to write entire process exit message"); ++ } else if ((result == -1) && (errno != EPIPE)) { ++ FATAL1("Failed to write exit code: %d", errno); ++ } ++ ProcessInfoList::RemoveProcess(pid); ++ { ++ MonitorLocker locker(monitor_); ++ process_count_--; ++ } ++ } ++ } else if (pid < 0) { ++ FATAL1("Wait for process exit failed: %d", errno); ++ } ++ } ++ } ++ ++ static bool terminate_done_; ++ static int process_count_; ++ static bool running_; ++ static Monitor* monitor_; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_IMPLICIT_CONSTRUCTORS(ExitCodeHandler); ++}; ++ ++bool ExitCodeHandler::running_ = false; ++int ExitCodeHandler::process_count_ = 0; ++bool ExitCodeHandler::terminate_done_ = false; ++Monitor* ExitCodeHandler::monitor_ = nullptr; ++ ++class ProcessStarter { ++ public: ++ ProcessStarter(Namespace* namespc, ++ const char* path, ++ char* arguments[], ++ intptr_t arguments_length, ++ const char* working_directory, ++ char* environment[], ++ intptr_t environment_length, ++ ProcessStartMode mode, ++ intptr_t* in, ++ intptr_t* out, ++ intptr_t* err, ++ intptr_t* id, ++ intptr_t* exit_event, ++ char** os_error_message) ++ : namespc_(namespc), ++ path_(path), ++ working_directory_(working_directory), ++ mode_(mode), ++ in_(in), ++ out_(out), ++ err_(err), ++ id_(id), ++ exit_event_(exit_event), ++ os_error_message_(os_error_message) { ++ read_in_[0] = -1; ++ read_in_[1] = -1; ++ read_err_[0] = -1; ++ read_err_[1] = -1; ++ write_out_[0] = -1; ++ write_out_[1] = -1; ++ exec_control_[0] = -1; ++ exec_control_[1] = -1; ++ ++ program_arguments_ = reinterpret_cast(Dart_ScopeAllocate( ++ (arguments_length + 2) * sizeof(*program_arguments_))); ++ program_arguments_[0] = const_cast(path_); ++ for (int i = 0; i < arguments_length; i++) { ++ program_arguments_[i + 1] = arguments[i]; ++ } ++ program_arguments_[arguments_length + 1] = NULL; ++ ++ program_environment_ = NULL; ++ if (environment != NULL) { ++ program_environment_ = reinterpret_cast(Dart_ScopeAllocate( ++ (environment_length + 1) * sizeof(*program_environment_))); ++ for (int i = 0; i < environment_length; i++) { ++ program_environment_[i] = environment[i]; ++ } ++ program_environment_[environment_length] = NULL; ++ } ++ } ++ ++ int Start() { ++ // Create pipes required. ++ int err = CreatePipes(); ++ if (err != 0) { ++ return err; ++ } ++ ++ // Fork to create the new process. ++ pid_t pid = TEMP_FAILURE_RETRY(fork()); ++ if (pid < 0) { ++ // Failed to fork. ++ return CleanupAndReturnError(); ++ } else if (pid == 0) { ++ // This runs in the new process. ++ NewProcess(); ++ } ++ ++ // This runs in the original process. ++ ++ // If the child process is not started in detached mode, be sure to ++ // listen for exit-codes, now that we have a non detached child process ++ // and also Register this child process. ++ if (Process::ModeIsAttached(mode_)) { ++ ExitCodeHandler::ProcessStarted(); ++ err = RegisterProcess(pid); ++ if (err != 0) { ++ return err; ++ } ++ } ++ ++ // Notify child process to start. This is done to delay the call to exec ++ // until the process is registered above, and we are ready to receive the ++ // exit code. ++ char msg = '1'; ++ int bytes_written = ++ FDUtils::WriteToBlocking(read_in_[1], &msg, sizeof(msg)); ++ if (bytes_written != sizeof(msg)) { ++ return CleanupAndReturnError(); ++ } ++ ++ // Read the result of executing the child process. ++ close(exec_control_[1]); ++ exec_control_[1] = -1; ++ if (Process::ModeIsAttached(mode_)) { ++ err = ReadExecResult(); ++ } else { ++ err = ReadDetachedExecResult(&pid); ++ } ++ close(exec_control_[0]); ++ exec_control_[0] = -1; ++ ++ // Return error code if any failures. ++ if (err != 0) { ++ if (Process::ModeIsAttached(mode_)) { ++ // Since exec() failed, we're not interested in the exit code. ++ // We close the reading side of the exit code pipe here. ++ // GetProcessExitCodes will get a broken pipe error when it ++ // tries to write to the writing side of the pipe and it will ++ // ignore the error. ++ close(*exit_event_); ++ *exit_event_ = -1; ++ } ++ CloseAllPipes(); ++ return err; ++ } ++ ++ if (Process::ModeHasStdio(mode_)) { ++ // Connect stdio, stdout and stderr. ++ FDUtils::SetNonBlocking(read_in_[0]); ++ *in_ = read_in_[0]; ++ close(read_in_[1]); ++ FDUtils::SetNonBlocking(write_out_[1]); ++ *out_ = write_out_[1]; ++ close(write_out_[0]); ++ FDUtils::SetNonBlocking(read_err_[0]); ++ *err_ = read_err_[0]; ++ close(read_err_[1]); ++ } else { ++ // Close all fds. ++ close(read_in_[0]); ++ close(read_in_[1]); ++ ASSERT(write_out_[0] == -1); ++ ASSERT(write_out_[1] == -1); ++ ASSERT(read_err_[0] == -1); ++ ASSERT(read_err_[1] == -1); ++ } ++ ASSERT(exec_control_[0] == -1); ++ ASSERT(exec_control_[1] == -1); ++ ++ *id_ = pid; ++ return 0; ++ } ++ ++ private: ++ static constexpr int kErrorBufferSize = 1024; ++ ++ int CreatePipes() { ++ int result; ++ result = TEMP_FAILURE_RETRY(pipe2(exec_control_, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ ++ // For a detached process the pipe to connect stdout is still used for ++ // signaling when to do the first fork. ++ result = TEMP_FAILURE_RETRY(pipe2(read_in_, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ ++ // For detached processes the pipe to connect stderr and stdin are not used. ++ if (Process::ModeHasStdio(mode_)) { ++ result = TEMP_FAILURE_RETRY(pipe2(read_err_, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ ++ result = TEMP_FAILURE_RETRY(pipe2(write_out_, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ } ++ ++ return 0; ++ } ++ ++ void NewProcess() { ++ // Wait for parent process before setting up the child process. ++ char msg; ++ int bytes_read = FDUtils::ReadFromBlocking(read_in_[0], &msg, sizeof(msg)); ++ if (bytes_read != sizeof(msg)) { ++ perror("Failed receiving notification message"); ++ _exit(1); ++ } ++ if (Process::ModeIsAttached(mode_)) { ++ ExecProcess(); ++ } else { ++ ExecDetachedProcess(); ++ } ++ } ++ ++ // Tries to find path_ relative to the current namespace unless it should be ++ // searched in the PATH. ++ // The path that should be passed to exec is returned in realpath. ++ // Returns true on success, and false if there was an error that should ++ // be reported to the parent. ++ bool FindPathInNamespace(char* realpath, intptr_t realpath_size) { ++ // Perform a PATH search if there's no slash in the path. ++ if (strchr(path_, '/') == NULL) { ++ // TODO(zra): If there is a non-default namespace, the entries in PATH ++ // should be treated as relative to the namespace. ++ strncpy(realpath, path_, realpath_size); ++ realpath[realpath_size - 1] = '\0'; ++ return true; ++ } ++ NamespaceScope ns(namespc_, path_); ++ const int fd = ++ TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_RDONLY | O_CLOEXEC)); ++ if (fd == -1) { ++ return false; ++ } ++ char procpath[PATH_MAX]; ++ snprintf(procpath, PATH_MAX, "/proc/self/fd/%d", fd); ++ const intptr_t length = ++ TEMP_FAILURE_RETRY(readlink(procpath, realpath, realpath_size)); ++ if (length < 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return false; ++ } ++ realpath[length] = '\0'; ++ FDUtils::SaveErrorAndClose(fd); ++ return true; ++ } ++ ++ void ExecProcess() { ++ if (mode_ == kNormal) { ++ if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ ++ if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ ++ if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ } else { ++ ASSERT(mode_ == kInheritStdio); ++ } ++ ++ if (working_directory_ != NULL && ++ !Directory::SetCurrent(namespc_, working_directory_)) { ++ ReportChildError(); ++ } ++ ++ if (program_environment_ != NULL) { ++ environ = program_environment_; ++ } ++ ++ char realpath[PATH_MAX]; ++ if (!FindPathInNamespace(realpath, PATH_MAX)) { ++ ReportChildError(); ++ } ++ // TODO(dart:io) Test for the existence of execveat, and use it instead. ++ execvp(realpath, const_cast(program_arguments_)); ++ ReportChildError(); ++ } ++ ++ void ExecDetachedProcess() { ++ if (mode_ == kDetached) { ++ ASSERT(write_out_[0] == -1); ++ ASSERT(write_out_[1] == -1); ++ ASSERT(read_err_[0] == -1); ++ ASSERT(read_err_[1] == -1); ++ // For a detached process the pipe to connect stdout is only used for ++ // signaling when to do the first fork. ++ close(read_in_[0]); ++ read_in_[0] = -1; ++ close(read_in_[1]); ++ read_in_[1] = -1; ++ } else { ++ // Don't close any fds if keeping stdio open to the detached process. ++ ASSERT(mode_ == kDetachedWithStdio); ++ } ++ // Fork once more to start a new session. ++ pid_t pid = TEMP_FAILURE_RETRY(fork()); ++ if (pid < 0) { ++ ReportChildError(); ++ } else if (pid == 0) { ++ // Start a new session. ++ if (TEMP_FAILURE_RETRY(setsid()) == -1) { ++ ReportChildError(); ++ } else { ++ // Do a final fork to not be the session leader. ++ pid = TEMP_FAILURE_RETRY(fork()); ++ if (pid < 0) { ++ ReportChildError(); ++ } else if (pid == 0) { ++ if (mode_ == kDetached) { ++ SetupDetached(); ++ } else { ++ SetupDetachedWithStdio(); ++ } ++ ++ if ((working_directory_ != NULL) && ++ !Directory::SetCurrent(namespc_, working_directory_)) { ++ ReportChildError(); ++ } ++ if (program_environment_ != NULL) { ++ environ = program_environment_; ++ } ++ ++ // Report the final PID and do the exec. ++ ReportPid(getpid()); // getpid cannot fail. ++ char realpath[PATH_MAX]; ++ if (!FindPathInNamespace(realpath, PATH_MAX)) { ++ ReportChildError(); ++ } ++ // TODO(dart:io) Test for the existence of execveat, and use it ++ // instead. ++ execvp(realpath, const_cast(program_arguments_)); ++ ReportChildError(); ++ } else { ++ // Exit the intermediate process. Avoid calling any atexit callbacks ++ // to avoid potential issues (e.g. deadlocks). ++ _exit(0); ++ } ++ } ++ } else { ++ // Exit the intermediate process. Avoid calling any atexit callbacks ++ // to avoid potential issues (e.g. deadlocks). ++ _exit(0); ++ } ++ } ++ ++ int RegisterProcess(pid_t pid) { ++ int result; ++ int event_fds[2]; ++ result = TEMP_FAILURE_RETRY(pipe2(event_fds, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ ++ ProcessInfoList::AddProcess(pid, event_fds[1]); ++ *exit_event_ = event_fds[0]; ++ FDUtils::SetNonBlocking(event_fds[0]); ++ return 0; ++ } ++ ++ int ReadExecResult() { ++ int child_errno; ++ int bytes_read = -1; ++ // Read exec result from child. If no data is returned the exec was ++ // successful and the exec call closed the pipe. Otherwise the errno ++ // is written to the pipe. ++ bytes_read = FDUtils::ReadFromBlocking(exec_control_[0], &child_errno, ++ sizeof(child_errno)); ++ if (bytes_read == sizeof(child_errno)) { ++ ReadChildError(); ++ return child_errno; ++ } else if (bytes_read == -1) { ++ return errno; ++ } ++ return 0; ++ } ++ ++ int ReadDetachedExecResult(pid_t* pid) { ++ int child_errno; ++ int bytes_read = -1; ++ // Read exec result from child. If only pid data is returned the exec was ++ // successful and the exec call closed the pipe. Otherwise the errno ++ // is written to the pipe as well. ++ int result[2]; ++ bytes_read = ++ FDUtils::ReadFromBlocking(exec_control_[0], result, sizeof(result)); ++ if (bytes_read == sizeof(int)) { ++ *pid = result[0]; ++ } else if (bytes_read == 2 * sizeof(int)) { ++ *pid = result[0]; ++ child_errno = result[1]; ++ ReadChildError(); ++ return child_errno; ++ } else if (bytes_read == -1) { ++ return errno; ++ } ++ return 0; ++ } ++ ++ void SetupDetached() { ++ ASSERT(mode_ == kDetached); ++ ++ // Close all open file descriptors except for exec_control_[1]. ++ int max_fds = sysconf(_SC_OPEN_MAX); ++ if (max_fds == -1) { ++ max_fds = _POSIX_OPEN_MAX; ++ } ++ for (int fd = 0; fd < max_fds; fd++) { ++ if (fd != exec_control_[1]) { ++ close(fd); ++ } ++ } ++ ++ // Re-open stdin, stdout and stderr and connect them to /dev/null. ++ // The loop above should already have closed all of them, so ++ // creating new file descriptors should start at STDIN_FILENO. ++ int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)); ++ if (fd != STDIN_FILENO) { ++ ReportChildError(); ++ } ++ if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDOUT_FILENO)) != ++ STDOUT_FILENO) { ++ ReportChildError(); ++ } ++ if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDERR_FILENO)) != ++ STDERR_FILENO) { ++ ReportChildError(); ++ } ++ } ++ ++ void SetupDetachedWithStdio() { ++ // Close all open file descriptors except for ++ // exec_control_[1], write_out_[0], read_in_[1] and ++ // read_err_[1]. ++ int max_fds = sysconf(_SC_OPEN_MAX); ++ if (max_fds == -1) { ++ max_fds = _POSIX_OPEN_MAX; ++ } ++ for (int fd = 0; fd < max_fds; fd++) { ++ if ((fd != exec_control_[1]) && (fd != write_out_[0]) && ++ (fd != read_in_[1]) && (fd != read_err_[1])) { ++ close(fd); ++ } ++ } ++ ++ if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ close(write_out_[0]); ++ ++ if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ close(read_in_[1]); ++ ++ if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ close(read_err_[1]); ++ } ++ ++ int CleanupAndReturnError() { ++ int actual_errno = errno; ++ // If CleanupAndReturnError is called without an actual errno make ++ // sure to return an error anyway. ++ if (actual_errno == 0) { ++ actual_errno = EPERM; ++ } ++ SetChildOsErrorMessage(); ++ CloseAllPipes(); ++ return actual_errno; ++ } ++ ++ void SetChildOsErrorMessage() { ++ char* error_message = DartUtils::ScopedCString(kErrorBufferSize); ++ Utils::StrError(errno, error_message, kErrorBufferSize); ++ *os_error_message_ = error_message; ++ } ++ ++ void ReportChildError() { ++ // In the case of failure in the child process write the errno and ++ // the OS error message to the exec control pipe and exit. ++ int child_errno = errno; ++ char error_buf[kErrorBufferSize]; ++ char* os_error_message = ++ Utils::StrError(errno, error_buf, kErrorBufferSize); ++ int bytes_written = FDUtils::WriteToBlocking(exec_control_[1], &child_errno, ++ sizeof(child_errno)); ++ if (bytes_written == sizeof(child_errno)) { ++ FDUtils::WriteToBlocking(exec_control_[1], os_error_message, ++ strlen(os_error_message) + 1); ++ } ++ close(exec_control_[1]); ++ ++ // We avoid running through registered atexit() handlers because that is ++ // unnecessary work. It can also cause deadlocks on exit in the forked ++ // process. ++ _exit(1); ++ } ++ ++ void ReportPid(int pid) { ++ // In the case of starting a detached process the actual pid of that process ++ // is communicated using the exec control pipe. ++ int bytes_written = ++ FDUtils::WriteToBlocking(exec_control_[1], &pid, sizeof(pid)); ++ ASSERT(bytes_written == sizeof(int)); ++ USE(bytes_written); ++ } ++ ++ void ReadChildError() { ++ char* message = DartUtils::ScopedCString(kErrorBufferSize); ++ if (message != NULL) { ++ FDUtils::ReadFromBlocking(exec_control_[0], message, kErrorBufferSize); ++ message[kErrorBufferSize - 1] = '\0'; ++ *os_error_message_ = message; ++ } else { ++ // Could not get error message. It will be NULL. ++ ASSERT(*os_error_message_ == NULL); ++ } ++ } ++ ++ void ClosePipe(int* fds) { ++ for (int i = 0; i < 2; i++) { ++ if (fds[i] != -1) { ++ close(fds[i]); ++ fds[i] = -1; ++ } ++ } ++ } ++ ++ void CloseAllPipes() { ++ ClosePipe(exec_control_); ++ ClosePipe(read_in_); ++ ClosePipe(read_err_); ++ ClosePipe(write_out_); ++ } ++ ++ int read_in_[2]; // Pipe for stdout to child process. ++ int read_err_[2]; // Pipe for stderr to child process. ++ int write_out_[2]; // Pipe for stdin to child process. ++ int exec_control_[2]; // Pipe to get the result from exec. ++ ++ char** program_arguments_; ++ char** program_environment_; ++ ++ Namespace* namespc_; ++ const char* path_; ++ const char* working_directory_; ++ ProcessStartMode mode_; ++ intptr_t* in_; ++ intptr_t* out_; ++ intptr_t* err_; ++ intptr_t* id_; ++ intptr_t* exit_event_; ++ char** os_error_message_; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessStarter); ++}; ++ ++int Process::Start(Namespace* namespc, ++ const char* path, ++ char* arguments[], ++ intptr_t arguments_length, ++ const char* working_directory, ++ char* environment[], ++ intptr_t environment_length, ++ ProcessStartMode mode, ++ intptr_t* in, ++ intptr_t* out, ++ intptr_t* err, ++ intptr_t* id, ++ intptr_t* exit_event, ++ char** os_error_message) { ++ ProcessStarter starter(namespc, path, arguments, arguments_length, ++ working_directory, environment, environment_length, ++ mode, in, out, err, id, exit_event, os_error_message); ++ return starter.Start(); ++} ++ ++static bool CloseProcessBuffers(struct pollfd* fds, int alive) { ++ int e = errno; ++ for (int i = 0; i < alive; i++) { ++ close(fds[i].fd); ++ } ++ errno = e; ++ return false; ++} ++ ++bool Process::Wait(intptr_t pid, ++ intptr_t in, ++ intptr_t out, ++ intptr_t err, ++ intptr_t exit_event, ++ ProcessResult* result) { ++ // Close input to the process right away. ++ close(in); ++ ++ // There is no return from this function using Dart_PropagateError ++ // as memory used by the buffer lists is freed through their ++ // destructors. ++ BufferList out_data; ++ BufferList err_data; ++ union { ++ uint8_t bytes[8]; ++ int32_t ints[2]; ++ } exit_code_data; ++ ++ struct pollfd fds[3]; ++ fds[0].fd = out; ++ fds[1].fd = err; ++ fds[2].fd = exit_event; ++ ++ for (int i = 0; i < 3; i++) { ++ fds[i].events = POLLIN; ++ } ++ ++ int alive = 3; ++ while (alive > 0) { ++ // Blocking call waiting for events from the child process. ++ if (TEMP_FAILURE_RETRY(poll(fds, alive, -1)) <= 0) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ ++ // Process incoming data. ++ for (int i = 0; i < alive; i++) { ++ if ((fds[i].revents & (POLLNVAL | POLLERR)) != 0) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ if ((fds[i].revents & POLLIN) != 0) { ++ intptr_t avail = FDUtils::AvailableBytes(fds[i].fd); ++ if (fds[i].fd == out) { ++ if (!out_data.Read(out, avail)) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ } else if (fds[i].fd == err) { ++ if (!err_data.Read(err, avail)) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ } else if (fds[i].fd == exit_event) { ++ if (avail == 8) { ++ intptr_t b = ++ TEMP_FAILURE_RETRY(read(exit_event, exit_code_data.bytes, 8)); ++ if (b != 8) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ } ++ } else { ++ UNREACHABLE(); ++ } ++ } ++ if ((fds[i].revents & POLLHUP) != 0) { ++ // Remove the pollfd from the list of pollfds. ++ close(fds[i].fd); ++ alive--; ++ if (i < alive) { ++ fds[i] = fds[alive]; ++ } ++ // Process the same index again. ++ i--; ++ continue; ++ } ++ } ++ } ++ ++ // All handles closed and all data read. ++ result->set_stdout_data(out_data.GetData()); ++ result->set_stderr_data(err_data.GetData()); ++ DEBUG_ASSERT(out_data.IsEmpty()); ++ DEBUG_ASSERT(err_data.IsEmpty()); ++ ++ // Calculate the exit code. ++ intptr_t exit_code = exit_code_data.ints[0]; ++ intptr_t negative = exit_code_data.ints[1]; ++ if (negative != 0) { ++ exit_code = -exit_code; ++ } ++ result->set_exit_code(exit_code); ++ ++ return true; ++} ++ ++bool Process::Kill(intptr_t id, int signal) { ++ return (TEMP_FAILURE_RETRY(kill(id, signal)) != -1); ++} ++ ++void Process::TerminateExitCodeHandler() { ++ ExitCodeHandler::TerminateExitCodeThread(); ++} ++ ++intptr_t Process::CurrentProcessId() { ++ return static_cast(getpid()); ++} ++ ++static void SaveErrorAndClose(FILE* file) { ++ int actual_errno = errno; ++ fclose(file); ++ errno = actual_errno; ++} ++ ++int64_t Process::CurrentRSS() { ++ // The second value in /proc/self/statm is the current RSS in pages. ++ // It is not possible to use getrusage() because the interested fields are not ++ // implemented by the linux kernel. ++ FILE* statm = fopen("/proc/self/statm", "r"); ++ if (statm == NULL) { ++ return -1; ++ } ++ int64_t current_rss_pages = 0; ++ int matches = fscanf(statm, "%*s%" Pd64 "", ¤t_rss_pages); ++ if (matches != 1) { ++ SaveErrorAndClose(statm); ++ return -1; ++ } ++ fclose(statm); ++ return current_rss_pages * getpagesize(); ++} ++ ++int64_t Process::MaxRSS() { ++ struct rusage usage; ++ usage.ru_maxrss = 0; ++ int r = getrusage(RUSAGE_SELF, &usage); ++ if (r < 0) { ++ return -1; ++ } ++ return usage.ru_maxrss * KB; ++} ++ ++static Mutex* signal_mutex = nullptr; ++static SignalInfo* signal_handlers = NULL; ++static const int kSignalsCount = 7; ++static const int kSignals[kSignalsCount] = { ++ SIGHUP, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGWINCH, ++ SIGQUIT // Allow VMService to listen on SIGQUIT. ++}; ++ ++SignalInfo::~SignalInfo() { ++ close(fd_); ++} ++ ++static void SignalHandler(int signal) { ++ MutexLocker lock(signal_mutex); ++ const SignalInfo* handler = signal_handlers; ++ while (handler != NULL) { ++ if (handler->signal() == signal) { ++ int value = 0; ++ VOID_TEMP_FAILURE_RETRY(write(handler->fd(), &value, 1)); ++ } ++ handler = handler->next(); ++ } ++} ++ ++intptr_t Process::SetSignalHandler(intptr_t signal) { ++ bool found = false; ++ for (int i = 0; i < kSignalsCount; i++) { ++ if (kSignals[i] == signal) { ++ found = true; ++ break; ++ } ++ } ++ if (!found) { ++ return -1; ++ } ++ int fds[2]; ++ if (NO_RETRY_EXPECTED(pipe2(fds, O_CLOEXEC)) != 0) { ++ return -1; ++ } ++ ThreadSignalBlocker blocker(kSignalsCount, kSignals); ++ MutexLocker lock(signal_mutex); ++ SignalInfo* handler = signal_handlers; ++ bool listen = true; ++ sa_handler_t oldact_handler = nullptr; ++ while (handler != NULL) { ++ if (handler->signal() == signal) { ++ oldact_handler = handler->oldact(); ++ listen = false; ++ break; ++ } ++ handler = handler->next(); ++ } ++ if (listen) { ++ struct sigaction act = {}; ++ act.sa_handler = SignalHandler; ++ sigemptyset(&act.sa_mask); ++ for (int i = 0; i < kSignalsCount; i++) { ++ sigaddset(&act.sa_mask, kSignals[i]); ++ } ++ struct sigaction oldact = {}; ++ int status = NO_RETRY_EXPECTED(sigaction(signal, &act, &oldact)); ++ if (status < 0) { ++ int err = errno; ++ close(fds[0]); ++ close(fds[1]); ++ errno = err; ++ return -1; ++ } ++ oldact_handler = oldact.sa_handler; ++ } ++ signal_handlers = ++ new SignalInfo(fds[1], signal, oldact_handler, signal_handlers); ++ return fds[0]; ++} ++ ++void Process::ClearSignalHandler(intptr_t signal, Dart_Port port) { ++ ThreadSignalBlocker blocker(kSignalsCount, kSignals); ++ MutexLocker lock(signal_mutex); ++ SignalInfo* handler = signal_handlers; ++ sa_handler_t oldact_handler = SIG_DFL; ++ bool any_removed = false; ++ bool any_remaining = false; ++ while (handler != NULL) { ++ bool remove = false; ++ if (handler->signal() == signal) { ++ if ((port == ILLEGAL_PORT) || (handler->port() == port)) { ++ if (signal_handlers == handler) { ++ signal_handlers = handler->next(); ++ } ++ handler->Unlink(); ++ remove = true; ++ oldact_handler = handler->oldact(); ++ any_removed = true; ++ } else { ++ any_remaining = true; ++ } ++ } ++ SignalInfo* next = handler->next(); ++ if (remove) { ++ delete handler; ++ } ++ handler = next; ++ } ++ if (any_removed && !any_remaining) { ++ struct sigaction act = {}; ++ act.sa_handler = oldact_handler; ++ VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, NULL)); ++ } ++} ++ ++void Process::ClearSignalHandlerByFd(intptr_t fd, Dart_Port port) { ++ ThreadSignalBlocker blocker(kSignalsCount, kSignals); ++ MutexLocker lock(signal_mutex); ++ SignalInfo* handler = signal_handlers; ++ sa_handler_t oldact_handler = SIG_DFL; ++ bool any_remaining = false; ++ intptr_t signal = -1; ++ while (handler != NULL) { ++ bool remove = false; ++ if (handler->fd() == fd) { ++ if ((port == ILLEGAL_PORT) || (handler->port() == port)) { ++ if (signal_handlers == handler) { ++ signal_handlers = handler->next(); ++ } ++ handler->Unlink(); ++ remove = true; ++ signal = handler->signal(); ++ } else { ++ any_remaining = true; ++ } ++ } ++ SignalInfo* next = handler->next(); ++ if (remove) { ++ delete handler; ++ } ++ handler = next; ++ } ++ if ((signal != -1) && !any_remaining) { ++ struct sigaction act = {}; ++ act.sa_handler = oldact_handler; ++ VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, NULL)); ++ } ++} ++ ++void ProcessInfoList::Init() { ++ active_processes_ = NULL; ++ ASSERT(ProcessInfoList::mutex_ == nullptr); ++ ProcessInfoList::mutex_ = new Mutex(); ++} ++ ++void ProcessInfoList::Cleanup() { ++ ASSERT(ProcessInfoList::mutex_ != nullptr); ++ delete ProcessInfoList::mutex_; ++ ProcessInfoList::mutex_ = nullptr; ++} ++ ++void ExitCodeHandler::Init() { ++ running_ = false; ++ process_count_ = 0; ++ terminate_done_ = false; ++ ASSERT(ExitCodeHandler::monitor_ == nullptr); ++ ExitCodeHandler::monitor_ = new Monitor(); ++} ++ ++void ExitCodeHandler::Cleanup() { ++ ASSERT(ExitCodeHandler::monitor_ != nullptr); ++ delete ExitCodeHandler::monitor_; ++ ExitCodeHandler::monitor_ = nullptr; ++} ++ ++void Process::Init() { ++ ExitCodeHandler::Init(); ++ ProcessInfoList::Init(); ++ ++ ASSERT(signal_mutex == nullptr); ++ signal_mutex = new Mutex(); ++ signal_handlers = NULL; ++ ++ ASSERT(Process::global_exit_code_mutex_ == nullptr); ++ Process::global_exit_code_mutex_ = new Mutex(); ++} ++ ++void Process::Cleanup() { ++ ClearAllSignalHandlers(); ++ ++ ASSERT(signal_mutex != nullptr); ++ delete signal_mutex; ++ signal_mutex = nullptr; ++ ++ ASSERT(Process::global_exit_code_mutex_ != nullptr); ++ delete Process::global_exit_code_mutex_; ++ Process::global_exit_code_mutex_ = nullptr; ++ ++ ProcessInfoList::Cleanup(); ++ ExitCodeHandler::Cleanup(); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/security_context_ohos.cc b/runtime/bin/security_context_ohos.cc +new file mode 100644 +index 00000000000..2ae284f0972 +--- /dev/null ++++ b/runtime/bin/security_context_ohos.cc +@@ -0,0 +1,88 @@ ++// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#if !defined(DART_IO_SECURE_SOCKET_DISABLED) ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/security_context.h" ++ ++#include ++#include ++#include ++ ++#include "bin/directory.h" ++#include "bin/file.h" ++#include "bin/secure_socket_filter.h" ++#include "bin/secure_socket_utils.h" ++#include "platform/syslog.h" ++ ++namespace dart { ++namespace bin { ++ ++// The security context won't necessarily use the compiled-in root certificates, ++// but since there is no way to update the size of the allocation after creating ++// the weak persistent handle, we assume that it will. Note that when the ++// root certs aren't compiled in, |root_certificates_pem_length| is 0. ++const intptr_t SSLCertContext::kApproximateSize = ++ sizeof(SSLCertContext) + root_certificates_pem_length; ++ ++void SSLCertContext::TrustBuiltinRoots() { ++ // First, try to use locations specified on the command line. ++ if (root_certs_file() != NULL) { ++ LoadRootCertFile(root_certs_file()); ++ return; ++ } ++ if (root_certs_cache() != NULL) { ++ LoadRootCertCache(root_certs_cache()); ++ return; ++ } ++ ++ if (bypass_trusting_system_roots()) { ++ if (SSL_LOG_STATUS) { ++ Syslog::Print("Bypass trusting Linux built-in roots\n"); ++ } ++ } else { ++ // On Linux, we use the compiled-in trusted certs as a last resort. First, ++ // we try to find the trusted certs in various standard locations. A good ++ // discussion of the complexities of this endeavor can be found here: ++ // ++ // https://www.happyassassin.net/2015/01/12/a-note-about-ssltls-trusted-certificate-stores-and-platforms/ ++ const char* bundle = "/etc/pki/tls/certs/ca-bundle.crt"; ++ const char* cachedir = "/etc/ssl/certs"; ++ if (File::Exists(NULL, bundle)) { ++ LoadRootCertFile(bundle); ++ return; ++ } ++ ++ if (Directory::Exists(NULL, cachedir) == Directory::EXISTS) { ++ LoadRootCertCache(cachedir); ++ return; ++ } ++ } ++ ++ // Fall back on the compiled-in certs if the standard locations don't exist, ++ // or we aren't on Linux. ++ if (SSL_LOG_STATUS) { ++ Syslog::Print("Trusting compiled-in roots\n"); ++ } ++ AddCompiledInCerts(); ++} ++ ++void SSLCertContext::RegisterCallbacks(SSL* ssl) { ++ // No callbacks to register for implementations using BoringSSL's built-in ++ // verification mechanism. ++} ++ ++TrustEvaluateHandlerFunc SSLCertContext::GetTrustEvaluateHandler() const { ++ return nullptr; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) ++ ++#endif // !defined(DART_IO_SECURE_SOCKET_DISABLED) +diff --git a/runtime/bin/socket_base_ohos.cc b/runtime/bin/socket_base_ohos.cc +new file mode 100644 +index 00000000000..eb4828d5972 +--- /dev/null ++++ b/runtime/bin/socket_base_ohos.cc +@@ -0,0 +1,148 @@ ++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/socket_base.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/fdutils.h" ++#include "bin/file.h" ++#include "bin/socket_base_ohos.h" ++#include "bin/thread.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++void SocketBase::GetError(intptr_t fd, OSError* os_error) { ++ int len = sizeof(errno); ++ int err = 0; ++ VOID_NO_RETRY_EXPECTED(getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, ++ reinterpret_cast(&len))); ++ errno = err; ++ os_error->SetCodeAndMessage(OSError::kSystem, errno); ++} ++ ++int SocketBase::GetType(intptr_t fd) { ++ struct stat64 buf; ++ int result = TEMP_FAILURE_RETRY(fstat64(fd, &buf)); ++ if (result == -1) { ++ return -1; ++ } ++ if (S_ISCHR(buf.st_mode)) { ++ return File::kTerminal; ++ } ++ if (S_ISFIFO(buf.st_mode)) { ++ return File::kPipe; ++ } ++ if (S_ISREG(buf.st_mode)) { ++ return File::kFile; ++ } ++ return File::kOther; ++} ++ ++AddressList* SocketBase::LookupAddress(const char* host, ++ int type, ++ OSError** os_error) { ++ // Perform a name lookup for a host name. ++ struct addrinfo hints; ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = SocketAddress::FromType(type); ++ hints.ai_socktype = SOCK_STREAM; ++ hints.ai_flags = AI_ADDRCONFIG; ++ hints.ai_protocol = IPPROTO_TCP; ++ struct addrinfo* info = NULL; ++ int status = NO_RETRY_EXPECTED(getaddrinfo(host, 0, &hints, &info)); ++ if (status != 0) { ++ // We failed, try without AI_ADDRCONFIG. This can happen when looking up ++ // e.g. '::1', when there are no global IPv6 addresses. ++ hints.ai_flags = 0; ++ status = NO_RETRY_EXPECTED(getaddrinfo(host, 0, &hints, &info)); ++ if (status != 0) { ++ ASSERT(*os_error == NULL); ++ *os_error = ++ new OSError(status, gai_strerror(status), OSError::kGetAddressInfo); ++ return NULL; ++ } ++ } ++ intptr_t count = 0; ++ for (struct addrinfo* c = info; c != NULL; c = c->ai_next) { ++ if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) { ++ count++; ++ } ++ } ++ intptr_t i = 0; ++ AddressList* addresses = new AddressList(count); ++ for (struct addrinfo* c = info; c != NULL; c = c->ai_next) { ++ if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) { ++ addresses->SetAt(i, new SocketAddress(c->ai_addr)); ++ i++; ++ } ++ } ++ freeaddrinfo(info); ++ return addresses; ++} ++ ++bool SocketBase::SetMulticastLoop(intptr_t fd, ++ intptr_t protocol, ++ bool enabled) { ++ int on = enabled ? 1 : 0; ++ int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6; ++ int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_LOOP ++ : IPV6_MULTICAST_LOOP; ++ return NO_RETRY_EXPECTED(setsockopt( ++ fd, level, optname, reinterpret_cast(&on), sizeof(on))) == ++ 0; ++} ++ ++bool SocketBase::GetOption(intptr_t fd, ++ int level, ++ int option, ++ char* data, ++ unsigned int* length) { ++ socklen_t optlen = static_cast(*length); ++ auto result = NO_RETRY_EXPECTED(getsockopt(fd, level, option, data, &optlen)); ++ *length = static_cast(optlen); ++ return result == 0; ++} ++ ++bool SocketBase::JoinMulticast(intptr_t fd, ++ const RawAddr& addr, ++ const RawAddr&, ++ int interfaceIndex) { ++ int proto = addr.addr.sa_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; ++ struct group_req mreq; ++ mreq.gr_interface = interfaceIndex; ++ memmove(&mreq.gr_group, &addr.ss, SocketAddress::GetAddrLength(addr)); ++ return NO_RETRY_EXPECTED( ++ setsockopt(fd, proto, MCAST_JOIN_GROUP, &mreq, sizeof(mreq))) == 0; ++} ++ ++bool SocketBase::LeaveMulticast(intptr_t fd, ++ const RawAddr& addr, ++ const RawAddr&, ++ int interfaceIndex) { ++ int proto = addr.addr.sa_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; ++ struct group_req mreq; ++ mreq.gr_interface = interfaceIndex; ++ memmove(&mreq.gr_group, &addr.ss, SocketAddress::GetAddrLength(addr)); ++ return NO_RETRY_EXPECTED(setsockopt(fd, proto, MCAST_LEAVE_GROUP, &mreq, ++ sizeof(mreq))) == 0; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/socket_base_ohos.h b/runtime/bin/socket_base_ohos.h +new file mode 100644 +index 00000000000..a961edfeee5 +--- /dev/null ++++ b/runtime/bin/socket_base_ohos.h +@@ -0,0 +1,17 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#ifndef RUNTIME_BIN_SOCKET_BASE_OHOS_H_ ++#define RUNTIME_BIN_SOCKET_BASE_OHOS_H_ ++ ++#if !defined(RUNTIME_BIN_SOCKET_BASE_H_) ++#error Do not include socket_base_linux.h directly. Use socket_base.h. ++#endif ++ ++#include ++#include ++#include ++#include ++ ++#endif // RUNTIME_BIN_SOCKET_BASE_OHOS_H_ +diff --git a/runtime/bin/socket_ohos.cc b/runtime/bin/socket_ohos.cc +new file mode 100644 +index 00000000000..0d4386b14c6 +--- /dev/null ++++ b/runtime/bin/socket_ohos.cc +@@ -0,0 +1,280 @@ ++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/socket.h" ++ ++#include // NOLINT ++ ++#include "bin/fdutils.h" ++#include "platform/signal_blocker.h" ++#include "platform/syslog.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++Socket::Socket(intptr_t fd) ++ : ReferenceCounted(), ++ fd_(fd), ++ isolate_port_(Dart_GetMainPortId()), ++ port_(ILLEGAL_PORT), ++ udp_receive_buffer_(NULL) {} ++ ++void Socket::CloseFd() { ++ SetClosedFd(); ++} ++ ++void Socket::SetClosedFd() { ++ fd_ = kClosedFd; ++} ++ ++static intptr_t Create(const RawAddr& addr) { ++ intptr_t fd; ++ intptr_t type = SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC; ++ fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, type, 0)); ++ if (fd < 0) { ++ return -1; ++ } ++ return fd; ++} ++ ++static intptr_t Connect(intptr_t fd, const RawAddr& addr) { ++ intptr_t result = TEMP_FAILURE_RETRY( ++ connect(fd, &addr.addr, SocketAddress::GetAddrLength(addr))); ++ if ((result == 0) || (errno == EINPROGRESS)) { ++ return fd; ++ } ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++} ++ ++intptr_t Socket::CreateConnect(const RawAddr& addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ return Connect(fd, addr); ++} ++ ++intptr_t Socket::CreateUnixDomainConnect(const RawAddr& addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ intptr_t result = TEMP_FAILURE_RETRY(connect( ++ fd, (struct sockaddr*)&addr.un, SocketAddress::GetAddrLength(addr))); ++ if (result == 0 || errno == EAGAIN) { ++ return fd; ++ } ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++} ++ ++intptr_t Socket::CreateBindConnect(const RawAddr& addr, ++ const RawAddr& source_addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ ++ intptr_t result = TEMP_FAILURE_RETRY( ++ bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr))); ++ if (result != 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ return Connect(fd, addr); ++} ++ ++intptr_t Socket::CreateUnixDomainBindConnect(const RawAddr& addr, ++ const RawAddr& source_addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ ++ intptr_t result = TEMP_FAILURE_RETRY( ++ bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr))); ++ if (result != 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ result = TEMP_FAILURE_RETRY(connect(fd, (struct sockaddr*)&addr.un, ++ SocketAddress::GetAddrLength(addr))); ++ if (result == 0 || errno == EAGAIN) { ++ return fd; ++ } ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++} ++ ++intptr_t Socket::CreateBindDatagram(const RawAddr& addr, ++ bool reuseAddress, ++ bool reusePort, ++ int ttl) { ++ intptr_t fd; ++ ++ fd = NO_RETRY_EXPECTED(socket(addr.addr.sa_family, ++ SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, ++ IPPROTO_UDP)); ++ if (fd < 0) { ++ return -1; ++ } ++ ++ if (reuseAddress) { ++ int optval = 1; ++ VOID_NO_RETRY_EXPECTED( ++ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))); ++ } ++ ++ if (reusePort) { ++#ifdef SO_REUSEPORT // Not all Linux versions support this. ++ int optval = 1; ++ int reuse_port_success = ++ setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); ++ // Even if it's defined, we might be running on a kernel ++ // that doesn't support it at runtime. ++ if (reuse_port_success != 0) { ++ if (errno == EINTR) { ++ FATAL("Unexpected EINTR errno"); ++ } ++ const int kBufferSize = 1024; ++ char error_buf[kBufferSize]; ++ Syslog::PrintErr("Dart Socket ERROR: %s:%d: %s.", __FILE__, __LINE__, ++ Utils::StrError(errno, error_buf, kBufferSize)); ++ } ++#else // !defined SO_REUSEPORT ++ Syslog::PrintErr( ++ "Dart Socket ERROR: %s:%d: `reusePort` not available on this Linux " ++ "version.", ++ __FILE__, __LINE__); ++#endif // SO_REUSEPORT ++ } ++ ++ if (!SocketBase::SetMulticastHops(fd, ++ addr.addr.sa_family == AF_INET ++ ? SocketAddress::TYPE_IPV4 ++ : SocketAddress::TYPE_IPV6, ++ ttl)) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ if (NO_RETRY_EXPECTED( ++ bind(fd, &addr.addr, SocketAddress::GetAddrLength(addr))) < 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ return fd; ++} ++ ++intptr_t ServerSocket::CreateBindListen(const RawAddr& addr, ++ intptr_t backlog, ++ bool v6_only) { ++ intptr_t fd; ++ ++ fd = NO_RETRY_EXPECTED( ++ socket(addr.ss.ss_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)); ++ if (fd < 0) { ++ return -1; ++ } ++ ++ int optval = 1; ++ VOID_NO_RETRY_EXPECTED( ++ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))); ++ ++ if (addr.ss.ss_family == AF_INET6) { ++ optval = v6_only ? 1 : 0; ++ VOID_NO_RETRY_EXPECTED( ++ setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval))); ++ } ++ ++ if (NO_RETRY_EXPECTED( ++ bind(fd, &addr.addr, SocketAddress::GetAddrLength(addr))) < 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ // Test for invalid socket port 65535 (some browsers disallow it). ++ if ((SocketAddress::GetAddrPort(addr) == 0) && ++ (SocketBase::GetPort(fd) == 65535)) { ++ // Don't close the socket until we have created a new socket, ensuring ++ // that we do not get the bad port number again. ++ intptr_t new_fd = CreateBindListen(addr, backlog, v6_only); ++ FDUtils::SaveErrorAndClose(fd); ++ return new_fd; ++ } ++ ++ if (NO_RETRY_EXPECTED(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ return fd; ++} ++ ++intptr_t ServerSocket::CreateUnixDomainBindListen(const RawAddr& addr, ++ intptr_t backlog) { ++ intptr_t fd = Create(addr); ++ if (NO_RETRY_EXPECTED(bind(fd, (struct sockaddr*)&addr.un, ++ SocketAddress::GetAddrLength(addr))) < 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ if (NO_RETRY_EXPECTED(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ return fd; ++} ++ ++bool ServerSocket::StartAccept(intptr_t fd) { ++ USE(fd); ++ return true; ++} ++ ++static bool IsTemporaryAcceptError(int error) { ++ // On Linux a number of protocol errors should be treated as EAGAIN. ++ // These are the ones for TCP/IP. ++ return (error == EAGAIN) || (error == ENETDOWN) || (error == EPROTO) || ++ (error == ENOPROTOOPT) || (error == EHOSTDOWN) || (error == ENONET) || ++ (error == EHOSTUNREACH) || (error == EOPNOTSUPP) || ++ (error == ENETUNREACH); ++} ++ ++intptr_t ServerSocket::Accept(intptr_t fd) { ++ intptr_t socket; ++ struct sockaddr clientaddr; ++ socklen_t addrlen = sizeof(clientaddr); ++ socket = TEMP_FAILURE_RETRY(accept(fd, &clientaddr, &addrlen)); ++ if (socket == -1) { ++ if (IsTemporaryAcceptError(errno)) { ++ // We need to signal to the caller that this is actually not an ++ // error. We got woken up from the poll on the listening socket, ++ // but there is no connection ready to be accepted. ++ ASSERT(kTemporaryFailure != -1); ++ socket = kTemporaryFailure; ++ } ++ } else { ++ if (!FDUtils::SetCloseOnExec(socket)) { ++ FDUtils::SaveErrorAndClose(socket); ++ return -1; ++ } ++ if (!FDUtils::SetNonBlocking(socket)) { ++ FDUtils::SaveErrorAndClose(socket); ++ return -1; ++ } ++ } ++ return socket; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/stdio_ohos.cc b/runtime/bin/stdio_ohos.cc +new file mode 100644 +index 00000000000..8dd67b67b45 +--- /dev/null ++++ b/runtime/bin/stdio_ohos.cc +@@ -0,0 +1,139 @@ ++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/stdio.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/fdutils.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool Stdin::ReadByte(intptr_t fd, int* byte) { ++ unsigned char b; ++ ssize_t s = TEMP_FAILURE_RETRY(read(fd, &b, 1)); ++ if (s < 0) { ++ return false; ++ } ++ *byte = (s == 0) ? -1 : b; ++ return true; ++} ++ ++bool Stdin::GetEchoMode(intptr_t fd, bool* enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ *enabled = ((term.c_lflag & ECHO) != 0); ++ return true; ++} ++ ++bool Stdin::SetEchoMode(intptr_t fd, bool enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ if (enabled) { ++ term.c_lflag |= ECHO; ++ } else { ++ term.c_lflag &= ~(ECHO); ++ } ++ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term)); ++ return (status == 0); ++} ++ ++bool Stdin::GetEchoNewlineMode(intptr_t fd, bool* enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ *enabled = ((term.c_lflag & ECHONL) != 0); ++ return true; ++} ++ ++bool Stdin::SetEchoNewlineMode(intptr_t fd, bool enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ if (enabled) { ++ term.c_lflag |= ECHONL; ++ } else { ++ term.c_lflag &= ~(ECHONL); ++ } ++ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term)); ++ return (status == 0); ++} ++ ++bool Stdin::GetLineMode(intptr_t fd, bool* enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ *enabled = ((term.c_lflag & ICANON) != 0); ++ return true; ++} ++ ++bool Stdin::SetLineMode(intptr_t fd, bool enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ if (enabled) { ++ term.c_lflag |= ICANON; ++ } else { ++ term.c_lflag &= ~(ICANON); ++ } ++ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term)); ++ return (status == 0); ++} ++ ++static bool TermIsKnownToSupportAnsi() { ++ const char* term = getenv("TERM"); ++ if (term == NULL) { ++ return false; ++ } ++ ++ return strstr(term, "xterm") != NULL || strstr(term, "screen") != NULL || ++ strstr(term, "rxvt") != NULL; ++} ++ ++bool Stdin::AnsiSupported(intptr_t fd, bool* supported) { ++ *supported = (isatty(fd) != 0) && TermIsKnownToSupportAnsi(); ++ return true; ++} ++ ++bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) { ++ struct winsize w; ++ int status = NO_RETRY_EXPECTED(ioctl(fd, TIOCGWINSZ, &w)); ++ if ((status == 0) && ((w.ws_col != 0) || (w.ws_row != 0))) { ++ size[0] = w.ws_col; ++ size[1] = w.ws_row; ++ return true; ++ } ++ return false; ++} ++ ++bool Stdout::AnsiSupported(intptr_t fd, bool* supported) { ++ *supported = (isatty(fd) != 0) && TermIsKnownToSupportAnsi(); ++ return true; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/sync_socket_ohos.cc b/runtime/bin/sync_socket_ohos.cc +new file mode 100644 +index 00000000000..be15707ba28 +--- /dev/null ++++ b/runtime/bin/sync_socket_ohos.cc +@@ -0,0 +1,92 @@ ++// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/sync_socket.h" ++ ++#include // NOLINT ++ ++#include "bin/fdutils.h" ++#include "bin/socket_base.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool SynchronousSocket::Initialize() { ++ // Nothing to do on Ohos. ++ return true; ++} ++ ++static intptr_t Create(const RawAddr& addr) { ++ intptr_t fd; ++ intptr_t type = SOCK_STREAM | SOCK_CLOEXEC; ++ fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, type, 0)); ++ if (fd < 0) { ++ return -1; ++ } ++ return fd; ++} ++ ++static intptr_t Connect(intptr_t fd, const RawAddr& addr) { ++ intptr_t result = TEMP_FAILURE_RETRY( ++ connect(fd, &addr.addr, SocketAddress::GetAddrLength(addr))); ++ if (result == 0) { ++ return fd; ++ } ++ ASSERT(errno != EINPROGRESS); ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++} ++ ++intptr_t SynchronousSocket::CreateConnect(const RawAddr& addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ return Connect(fd, addr); ++} ++ ++intptr_t SynchronousSocket::Available(intptr_t fd) { ++ return SocketBase::Available(fd); ++} ++ ++intptr_t SynchronousSocket::GetPort(intptr_t fd) { ++ return SocketBase::GetPort(fd); ++} ++ ++SocketAddress* SynchronousSocket::GetRemotePeer(intptr_t fd, intptr_t* port) { ++ return SocketBase::GetRemotePeer(fd, port); ++} ++ ++intptr_t SynchronousSocket::Read(intptr_t fd, ++ void* buffer, ++ intptr_t num_bytes) { ++ return SocketBase::Read(fd, buffer, num_bytes, SocketBase::kSync); ++} ++ ++intptr_t SynchronousSocket::Write(intptr_t fd, ++ const void* buffer, ++ intptr_t num_bytes) { ++ return SocketBase::Write(fd, buffer, num_bytes, SocketBase::kSync); ++} ++ ++void SynchronousSocket::ShutdownRead(intptr_t fd) { ++ VOID_NO_RETRY_EXPECTED(shutdown(fd, SHUT_RD)); ++} ++ ++void SynchronousSocket::ShutdownWrite(intptr_t fd) { ++ VOID_NO_RETRY_EXPECTED(shutdown(fd, SHUT_WR)); ++} ++ ++void SynchronousSocket::Close(intptr_t fd) { ++ return SocketBase::Close(fd); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/thread_ohos.cc b/runtime/bin/thread_ohos.cc +new file mode 100644 +index 00000000000..eaaf6684303 +--- /dev/null ++++ b/runtime/bin/thread_ohos.cc +@@ -0,0 +1,280 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL) ++ ++#include "bin/thread.h" ++#include "bin/thread_ohos.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "platform/assert.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++#define VALIDATE_PTHREAD_RESULT(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ FATAL2("pthread error: %d (%s)", result, \ ++ Utils::StrError(result, error_buf, kBufferSize)); \ ++ } ++ ++#ifdef DEBUG ++#define RETURN_ON_PTHREAD_FAILURE(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ fprintf(stderr, "%s:%d: pthread error: %d (%s)\n", __FILE__, __LINE__, \ ++ result, Utils::StrError(result, error_buf, kBufferSize)); \ ++ return result; \ ++ } ++#else ++#define RETURN_ON_PTHREAD_FAILURE(result) \ ++ if (result != 0) { \ ++ return result; \ ++ } ++#endif ++ ++static void ComputeTimeSpecMicros(struct timespec* ts, int64_t micros) { ++ int64_t secs = micros / kMicrosecondsPerSecond; ++ int64_t nanos = ++ (micros - (secs * kMicrosecondsPerSecond)) * kNanosecondsPerMicrosecond; ++ int result = clock_gettime(CLOCK_MONOTONIC, ts); ++ ASSERT(result == 0); ++ ts->tv_sec += secs; ++ ts->tv_nsec += nanos; ++ if (ts->tv_nsec >= kNanosecondsPerSecond) { ++ ts->tv_sec += 1; ++ ts->tv_nsec -= kNanosecondsPerSecond; ++ } ++} ++ ++class ThreadStartData { ++ public: ++ ThreadStartData(const char* name, ++ Thread::ThreadStartFunction function, ++ uword parameter) ++ : name_(name), function_(function), parameter_(parameter) {} ++ ++ const char* name() const { return name_; } ++ Thread::ThreadStartFunction function() const { return function_; } ++ uword parameter() const { return parameter_; } ++ ++ private: ++ const char* name_; ++ Thread::ThreadStartFunction function_; ++ uword parameter_; ++ ++ DISALLOW_COPY_AND_ASSIGN(ThreadStartData); ++}; ++ ++// Dispatch to the thread start function provided by the caller. This trampoline ++// is used to ensure that the thread is properly destroyed if the thread just ++// exits. ++static void* ThreadStart(void* data_ptr) { ++ ThreadStartData* data = reinterpret_cast(data_ptr); ++ ++ const char* name = data->name(); ++ Thread::ThreadStartFunction function = data->function(); ++ uword parameter = data->parameter(); ++ delete data; ++ ++ // Set the thread name. There is 16 bytes limit on the name (including \0). ++ // pthread_setname_np ignores names that are too long rather than truncating. ++ char truncated_name[16]; ++ snprintf(truncated_name, sizeof(truncated_name), "%s", name); ++ pthread_setname_np(pthread_self(), truncated_name); ++ ++ // Call the supplied thread start function handing it its parameters. ++ function(parameter); ++ ++ return NULL; ++} ++ ++int Thread::Start(const char* name, ++ ThreadStartFunction function, ++ uword parameter) { ++ pthread_attr_t attr; ++ int result = pthread_attr_init(&attr); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_setstacksize(&attr, Thread::GetMaxStackSize()); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ ThreadStartData* data = new ThreadStartData(name, function, parameter); ++ ++ pthread_t tid; ++ result = pthread_create(&tid, &attr, ThreadStart, data); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_destroy(&attr); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ return 0; ++} ++ ++const ThreadId Thread::kInvalidThreadId = static_cast(0); ++ ++intptr_t Thread::GetMaxStackSize() { ++ const int kStackSize = (128 * kWordSize * KB); ++ return kStackSize; ++} ++ ++ThreadId Thread::GetCurrentThreadId() { ++ return pthread_self(); ++} ++ ++bool Thread::Compare(ThreadId a, ThreadId b) { ++ return (pthread_equal(a, b) != 0); ++} ++ ++Mutex::Mutex() { ++ pthread_mutexattr_t attr; ++ int result = pthread_mutexattr_init(&attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); ++ VALIDATE_PTHREAD_RESULT(result); ++#endif // defined(DEBUG) ++ ++ result = pthread_mutex_init(data_.mutex(), &attr); ++ // Verify that creating a pthread_mutex succeeded. ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_mutexattr_destroy(&attr); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++Mutex::~Mutex() { ++ int result = pthread_mutex_destroy(data_.mutex()); ++ // Verify that the pthread_mutex was destroyed. ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void Mutex::Lock() { ++ int result = pthread_mutex_lock(data_.mutex()); ++ // Specifically check for dead lock to help debugging. ++ ASSERT(result != EDEADLK); ++ ASSERT(result == 0); // Verify no other errors. ++ // TODO(iposva): Do we need to track lock owners? ++} ++ ++bool Mutex::TryLock() { ++ int result = pthread_mutex_trylock(data_.mutex()); ++ // Return false if the lock is busy and locking failed. ++ if (result == EBUSY) { ++ return false; ++ } ++ ASSERT(result == 0); // Verify no other errors. ++ // TODO(iposva): Do we need to track lock owners? ++ return true; ++} ++ ++void Mutex::Unlock() { ++ // TODO(iposva): Do we need to track lock owners? ++ int result = pthread_mutex_unlock(data_.mutex()); ++ // Specifically check for wrong thread unlocking to aid debugging. ++ ASSERT(result != EPERM); ++ ASSERT(result == 0); // Verify no other errors. ++} ++ ++Monitor::Monitor() { ++ pthread_mutexattr_t mutex_attr; ++ int result = pthread_mutexattr_init(&mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ result = pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK); ++ VALIDATE_PTHREAD_RESULT(result); ++#endif // defined(DEBUG) ++ ++ result = pthread_mutex_init(data_.mutex(), &mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_mutexattr_destroy(&mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ pthread_condattr_t cond_attr; ++ result = pthread_condattr_init(&cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_cond_init(data_.cond(), &cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_condattr_destroy(&cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++Monitor::~Monitor() { ++ int result = pthread_mutex_destroy(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_cond_destroy(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void Monitor::Enter() { ++ int result = pthread_mutex_lock(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ // TODO(iposva): Do we need to track lock owners? ++} ++ ++void Monitor::Exit() { ++ // TODO(iposva): Do we need to track lock owners? ++ int result = pthread_mutex_unlock(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++Monitor::WaitResult Monitor::Wait(int64_t millis) { ++ return WaitMicros(millis * kMicrosecondsPerMillisecond); ++} ++ ++Monitor::WaitResult Monitor::WaitMicros(int64_t micros) { ++ // TODO(iposva): Do we need to track lock owners? ++ Monitor::WaitResult retval = kNotified; ++ if (micros == kNoTimeout) { ++ // Wait forever. ++ int result = pthread_cond_wait(data_.cond(), data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ } else { ++ struct timespec ts; ++ ComputeTimeSpecMicros(&ts, micros); ++ int result = pthread_cond_timedwait(data_.cond(), data_.mutex(), &ts); ++ ASSERT((result == 0) || (result == ETIMEDOUT)); ++ if (result == ETIMEDOUT) { ++ retval = kTimedOut; ++ } ++ } ++ return retval; ++} ++ ++void Monitor::Notify() { ++ // TODO(iposva): Do we need to track lock owners? ++ int result = pthread_cond_signal(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void Monitor::NotifyAll() { ++ // TODO(iposva): Do we need to track lock owners? ++ int result = pthread_cond_broadcast(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL) +diff --git a/runtime/bin/thread_ohos.h b/runtime/bin/thread_ohos.h +new file mode 100644 +index 00000000000..2d2b5fbb12c +--- /dev/null ++++ b/runtime/bin/thread_ohos.h +@@ -0,0 +1,57 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#ifndef RUNTIME_BIN_THREAD_OHOS_H_ ++#define RUNTIME_BIN_THREAD_OHOS_H_ ++ ++#if !defined(RUNTIME_BIN_THREAD_H_) ++#error Do not include thread_linux.h directly; use thread.h instead. ++#endif ++ ++#include ++ ++#include "platform/assert.h" ++#include "platform/globals.h" ++ ++namespace dart { ++namespace bin { ++ ++typedef pthread_t ThreadId; ++ ++class MutexData { ++ private: ++ MutexData() {} ++ ~MutexData() {} ++ ++ pthread_mutex_t* mutex() { return &mutex_; } ++ ++ pthread_mutex_t mutex_; ++ ++ friend class Mutex; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(MutexData); ++}; ++ ++class MonitorData { ++ private: ++ MonitorData() {} ++ ~MonitorData() {} ++ ++ pthread_mutex_t* mutex() { return &mutex_; } ++ pthread_cond_t* cond() { return &cond_; } ++ ++ pthread_mutex_t mutex_; ++ pthread_cond_t cond_; ++ ++ friend class Monitor; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(MonitorData); ++}; ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // RUNTIME_BIN_THREAD_OHOS_H_ +diff --git a/runtime/bin/utils_ohos.cc b/runtime/bin/utils_ohos.cc +new file mode 100644 +index 00000000000..487e4f30199 +--- /dev/null ++++ b/runtime/bin/utils_ohos.cc +@@ -0,0 +1,113 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/utils.h" ++#include "platform/assert.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++OSError::OSError() : sub_system_(kSystem), code_(0), message_(NULL) { ++ Reload(); ++} ++ ++void OSError::Reload() { ++ SetCodeAndMessage(kSystem, errno); ++} ++ ++void OSError::SetCodeAndMessage(SubSystem sub_system, int code) { ++ set_sub_system(sub_system); ++ set_code(code); ++ if (sub_system == kSystem) { ++ const int kBufferSize = 1024; ++ char error_buf[kBufferSize]; ++ SetMessage(Utils::StrError(code, error_buf, kBufferSize)); ++ } else if (sub_system == kGetAddressInfo) { ++ SetMessage(gai_strerror(code)); ++ } else { ++ UNREACHABLE(); ++ } ++} ++ ++const char* StringUtils::ConsoleStringToUtf8(const char* str, ++ intptr_t len, ++ intptr_t* result_len) { ++ return NULL; ++} ++ ++const char* StringUtils::Utf8ToConsoleString(const char* utf8, ++ intptr_t len, ++ intptr_t* result_len) { ++ return NULL; ++} ++ ++char* StringUtils::ConsoleStringToUtf8(char* str, ++ intptr_t len, ++ intptr_t* result_len) { ++ return NULL; ++} ++ ++char* StringUtils::Utf8ToConsoleString(char* utf8, ++ intptr_t len, ++ intptr_t* result_len) { ++ return NULL; ++} ++ ++bool ShellUtils::GetUtf8Argv(int argc, char** argv) { ++ return false; ++} ++ ++void TimerUtils::InitOnce() {} ++ ++int64_t TimerUtils::GetCurrentMonotonicMillis() { ++ return GetCurrentMonotonicMicros() / 1000; ++} ++ ++int64_t TimerUtils::GetCurrentMonotonicMicros() { ++ struct timespec ts; ++ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { ++ UNREACHABLE(); ++ return 0; ++ } ++ // Convert to microseconds. ++ int64_t result = ts.tv_sec; ++ result *= kMicrosecondsPerSecond; ++ result += (ts.tv_nsec / kNanosecondsPerMicrosecond); ++ return result; ++} ++ ++void TimerUtils::Sleep(int64_t millis) { ++ struct timespec req; // requested. ++ struct timespec rem; // remainder. ++ int64_t micros = millis * kMicrosecondsPerMillisecond; ++ int64_t seconds = micros / kMicrosecondsPerSecond; ++ micros = micros - seconds * kMicrosecondsPerSecond; ++ int64_t nanos = micros * kNanosecondsPerMicrosecond; ++ req.tv_sec = seconds; ++ req.tv_nsec = nanos; ++ while (true) { ++ int r = nanosleep(&req, &rem); ++ if (r == 0) { ++ break; ++ } ++ // We should only ever see an interrupt error. ++ ASSERT(errno == EINTR); ++ // Copy remainder into requested and repeat. ++ req = rem; ++ } ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/vmservice_impl.cc b/runtime/bin/vmservice_impl.cc +index d7296c98e0f..d1abac994ff 100644 +--- a/runtime/bin/vmservice_impl.cc ++++ b/runtime/bin/vmservice_impl.cc +@@ -127,6 +127,9 @@ bool VmService::Setup(const char* server_ip, + + Dart_Handle result; + ++ //TODO 临时关闭验证 ++ auth_codes_disabled=true; ++ + // Prepare builtin and its dependent libraries for use to resolve URIs. + // Set up various closures, e.g: printing, timers etc. + // Set up 'package root' for URI resolution. +diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h +index 9c8d00fc657..7122f9f7d08 100644 +--- a/runtime/platform/globals.h ++++ b/runtime/platform/globals.h +@@ -383,7 +383,8 @@ struct simd128_value_t { + + #if !defined(DART_TARGET_OS_ANDROID) && !defined(DART_TARGET_OS_FUCHSIA) && \ + !defined(DART_TARGET_OS_MACOS_IOS) && !defined(DART_TARGET_OS_LINUX) && \ +- !defined(DART_TARGET_OS_MACOS) && !defined(DART_TARGET_OS_WINDOWS) ++ !defined(DART_TARGET_OS_MACOS) && !defined(DART_TARGET_OS_WINDOWS) && \ ++ !defined(DART_TARGET_OS_OHOS) + // No target OS specified; pick the one matching the host OS. + #if defined(DART_HOST_OS_ANDROID) + #define DART_TARGET_OS_ANDROID 1 +@@ -394,6 +395,8 @@ struct simd128_value_t { + #define DART_TARGET_OS_MACOS_IOS 1 + #elif defined(DART_HOST_OS_LINUX) + #define DART_TARGET_OS_LINUX 1 ++#elif defined(DART_HOST_OS_OHOS) ++#define DART_TARGET_OS_OHOS 1 + #elif defined(DART_HOST_OS_MACOS) + #define DART_TARGET_OS_MACOS 1 + #elif defined(DART_HOST_OS_WINDOWS) +@@ -789,6 +792,8 @@ DART_FORCE_INLINE D bit_copy(const S& source) { + #define kTargetOperatingSystemName "fuchsia" + #elif defined(DART_TARGET_OS_LINUX) + #define kTargetOperatingSystemName "linux" ++#elif defined(DART_TARGET_OS_OHOS) ++#define kTargetOperatingSystemName "ohos" + #elif defined(DART_TARGET_OS_MACOS_IOS) + #define kTargetOperatingSystemName "ios" + #elif defined(DART_TARGET_OS_MACOS) +diff --git a/runtime/platform/platform_sources.gni b/runtime/platform/platform_sources.gni +index bef56feb093..50f90cc76dc 100644 +--- a/runtime/platform/platform_sources.gni ++++ b/runtime/platform/platform_sources.gni +@@ -25,6 +25,7 @@ platform_sources = [ + "syslog_android.cc", + "syslog_fuchsia.cc", + "syslog_linux.cc", ++ "syslog_ohos.cc", + "syslog_macos.cc", + "syslog_win.cc", + "text_buffer.cc", +@@ -37,6 +38,7 @@ platform_sources = [ + "utils_android.cc", + "utils_fuchsia.cc", + "utils_linux.cc", ++ "utils_ohos.cc", + "utils_macos.cc", + "utils_win.cc", + ] +diff --git a/runtime/platform/syslog_ohos.cc b/runtime/platform/syslog_ohos.cc +new file mode 100644 +index 00000000000..3dcbc463dc6 +--- /dev/null ++++ b/runtime/platform/syslog_ohos.cc +@@ -0,0 +1,26 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "platform/syslog.h" ++ ++#include // NOLINT ++ ++namespace dart { ++ ++void Syslog::VPrint(const char* format, va_list args) { ++ vfprintf(stdout, format, args); ++ fflush(stdout); ++} ++ ++void Syslog::VPrintErr(const char* format, va_list args) { ++ vfprintf(stderr, format, args); ++ fflush(stderr); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/platform/utils_ohos.cc b/runtime/platform/utils_ohos.cc +new file mode 100644 +index 00000000000..806796a5935 +--- /dev/null ++++ b/runtime/platform/utils_ohos.cc +@@ -0,0 +1,54 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "platform/utils.h" ++#include "platform/utils_ohos.h" ++ ++namespace dart { ++ ++char* Utils::StrNDup(const char* s, intptr_t n) { ++ return strndup(s, n); ++} ++ ++char* Utils::StrDup(const char* s) { ++ return strdup(s); ++} ++ ++intptr_t Utils::StrNLen(const char* s, intptr_t n) { ++ return strnlen(s, n); ++} ++ ++int Utils::SNPrint(char* str, size_t size, const char* format, ...) { ++ va_list args; ++ va_start(args, format); ++ int retval = VSNPrint(str, size, format, args); ++ va_end(args); ++ return retval; ++} ++ ++int Utils::VSNPrint(char* str, size_t size, const char* format, va_list args) { ++ MSAN_UNPOISON(str, size); ++ int retval = vsnprintf(str, size, format, args); ++ if (retval < 0) { ++ FATAL1("Fatal error in Utils::VSNPrint with format '%s'", format); ++ } ++ return retval; ++} ++ ++int Utils::Close(int fildes) { ++ return close(fildes); ++} ++size_t Utils::Read(int filedes, void* buf, size_t nbyte) { ++ return read(filedes, buf, nbyte); ++} ++int Utils::Unlink(const char* path) { ++ return unlink(path); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/platform/utils_ohos.h b/runtime/platform/utils_ohos.h +new file mode 100644 +index 00000000000..b33c3291826 +--- /dev/null ++++ b/runtime/platform/utils_ohos.h +@@ -0,0 +1,56 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#ifndef RUNTIME_PLATFORM_UTILS_OHOS_H_ ++#define RUNTIME_PLATFORM_UTILS_OHOS_H_ ++ ++#if !defined(RUNTIME_PLATFORM_UTILS_H_) ++#error Do not include utils_linux.h directly; use utils.h instead. ++#endif ++ ++#include // NOLINT ++ ++namespace dart { ++ ++inline uint16_t Utils::HostToBigEndian16(uint16_t value) { ++ return htobe16(value); ++} ++ ++inline uint32_t Utils::HostToBigEndian32(uint32_t value) { ++ return htobe32(value); ++} ++ ++inline uint64_t Utils::HostToBigEndian64(uint64_t value) { ++ return htobe64(value); ++} ++ ++inline uint16_t Utils::HostToLittleEndian16(uint16_t value) { ++ return htole16(value); ++} ++ ++inline uint32_t Utils::HostToLittleEndian32(uint32_t value) { ++ return htole32(value); ++} ++ ++inline uint64_t Utils::HostToLittleEndian64(uint64_t value) { ++ return htole64(value); ++} ++ ++inline char* Utils::StrError(int err, char* buffer, size_t bufsize) { ++#if !defined(__GLIBC__) || \ ++ ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) ++ // Use the XSI version. ++ if (strerror_r(err, buffer, bufsize) != 0) { ++ snprintf(buffer, bufsize, "%s", "strerror_r failed"); ++ } ++ return buffer; ++#else ++ // Use the GNU specific version. ++ return strerror_r(err, buffer, bufsize); ++#endif ++} ++ ++} // namespace dart ++ ++#endif // RUNTIME_PLATFORM_UTILS_OHOS_H_ +diff --git a/runtime/vm/compiler/ffi/abi.cc b/runtime/vm/compiler/ffi/abi.cc +index 66fa5a8d564..cc5eb834b3d 100644 +--- a/runtime/vm/compiler/ffi/abi.cc ++++ b/runtime/vm/compiler/ffi/abi.cc +@@ -53,6 +53,8 @@ static_assert(offsetof(AbiAlignmentUint64, i) == 8, + #define DART_TARGET_OS_NAME Fuchsia + #elif defined(DART_TARGET_OS_LINUX) + #define DART_TARGET_OS_NAME Linux ++#elif defined(DART_TARGET_OS_OHOS) ++#define DART_TARGET_OS_NAME Ohos + #elif defined(DART_TARGET_OS_MACOS) + #if DART_TARGET_OS_MACOS_IOS + #define DART_TARGET_OS_NAME IOS +diff --git a/runtime/vm/compiler/ffi/abi.h b/runtime/vm/compiler/ffi/abi.h +index b693ef85255..54b15f48890 100644 +--- a/runtime/vm/compiler/ffi/abi.h ++++ b/runtime/vm/compiler/ffi/abi.h +@@ -24,6 +24,8 @@ enum class Abi { + kAndroidArm64, + kAndroidIA32, + kAndroidX64, ++ kOhosArm, ++ kOhosArm64, + kFuchsiaArm64, + kFuchsiaX64, + kIOSArm, +@@ -49,9 +51,9 @@ const int64_t num_abis = static_cast(Abi::kWindowsX64) + 1; + // - runtime/vm/compiler/frontend/kernel_to_il.cc + static_assert(static_cast(Abi::kAndroidArm) == 0, + "Enum value unexpected."); +-static_assert(static_cast(Abi::kWindowsX64) == 19, ++static_assert(static_cast(Abi::kWindowsX64) == 21, + "Enum value unexpected."); +-static_assert(num_abis == 20, "Enum value unexpected."); ++static_assert(num_abis == 22, "Enum value unexpected."); + + // The target ABI. Defines sizes and alignment of native types. + Abi TargetAbi(); +diff --git a/runtime/vm/compiler/ffi/native_calling_convention.cc b/runtime/vm/compiler/ffi/native_calling_convention.cc +index 001605f6163..11529096e4e 100644 +--- a/runtime/vm/compiler/ffi/native_calling_convention.cc ++++ b/runtime/vm/compiler/ffi/native_calling_convention.cc +@@ -31,7 +31,7 @@ static bool SoftFpAbi() { + } + #else // !defined(FFI_UNIT_TESTS) + static bool SoftFpAbi() { +-#if defined(TARGET_ARCH_ARM) && defined(DART_TARGET_OS_ANDROID) ++#if defined(TARGET_ARCH_ARM) && (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_OHOS)) + return true; + #else + return false; +diff --git a/runtime/vm/compiler/ffi/native_calling_convention_test.cc b/runtime/vm/compiler/ffi/native_calling_convention_test.cc +index 707d9e845db..08a0930de5d 100644 +--- a/runtime/vm/compiler/ffi/native_calling_convention_test.cc ++++ b/runtime/vm/compiler/ffi/native_calling_convention_test.cc +@@ -658,7 +658,7 @@ UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_regress46127) { + RunSignatureTest(Z, "regress46127", arguments, struct_type); + + #if defined(TARGET_ARCH_IA32) && \ +- (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX)) ++ (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX)) || defined(DART_TARGET_OS_OHOS) + // We must count the result pointer passed on the stack as well. + EXPECT_EQ(4, native_calling_convention.StackTopInBytes()); + #else +diff --git a/runtime/vm/cpu_arm.cc b/runtime/vm/cpu_arm.cc +index ce9a8ffbca3..04c3aefb682 100644 +--- a/runtime/vm/cpu_arm.cc ++++ b/runtime/vm/cpu_arm.cc +@@ -59,7 +59,7 @@ DEFINE_FLAG(bool, + "Use integer division instruction if supported"); + + #if defined(TARGET_HOST_MISMATCH) +-#if defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS_IOS) ++#if defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS_IOS) || defined(DART_TARGET_OS_OHOS) + DEFINE_FLAG(bool, sim_use_hardfp, false, "Use the hardfp ABI."); + #else + DEFINE_FLAG(bool, sim_use_hardfp, true, "Use the hardfp ABI."); +diff --git a/runtime/vm/cpuinfo_ohos.cc b/runtime/vm/cpuinfo_ohos.cc +new file mode 100644 +index 00000000000..e5d90ac5dfb +--- /dev/null ++++ b/runtime/vm/cpuinfo_ohos.cc +@@ -0,0 +1,107 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "vm/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "vm/cpuid.h" ++#include "vm/cpuinfo.h" ++#include "vm/proccpuinfo.h" ++ ++#include "platform/assert.h" ++ ++// As with Windows, on IA32 and X64, we use the cpuid instruction. ++// The analogous instruction is privileged on ARM, so we resort to ++// reading from /proc/cpuinfo. ++ ++namespace dart { ++ ++CpuInfoMethod CpuInfo::method_ = kCpuInfoDefault; ++const char* CpuInfo::fields_[kCpuInfoMax] = {0}; ++ ++void CpuInfo::Init() { ++#if defined(HOST_ARCH_IA32) || defined(HOST_ARCH_X64) ++ fields_[kCpuInfoProcessor] = "vendor_id"; ++ fields_[kCpuInfoModel] = "model name"; ++ fields_[kCpuInfoHardware] = "model name"; ++ fields_[kCpuInfoFeatures] = "flags"; ++ fields_[kCpuInfoArchitecture] = "CPU architecture"; ++ method_ = kCpuInfoCpuId; ++ CpuId::Init(); ++#elif defined(HOST_ARCH_ARM) ++ fields_[kCpuInfoProcessor] = "Processor"; ++ fields_[kCpuInfoModel] = "model name"; ++ fields_[kCpuInfoHardware] = "Hardware"; ++ fields_[kCpuInfoFeatures] = "Features"; ++ fields_[kCpuInfoArchitecture] = "CPU architecture"; ++ method_ = kCpuInfoSystem; ++ ProcCpuInfo::Init(); ++#elif defined(HOST_ARCH_ARM64) ++ fields_[kCpuInfoProcessor] = "Processor"; ++ fields_[kCpuInfoModel] = "CPU implementer"; ++ fields_[kCpuInfoHardware] = "CPU implementer"; ++ fields_[kCpuInfoFeatures] = "Features"; ++ fields_[kCpuInfoArchitecture] = "CPU architecture"; ++ method_ = kCpuInfoSystem; ++ ProcCpuInfo::Init(); ++#elif defined(HOST_ARCH_RISCV32) || defined(HOST_ARCH_RISCV64) ++ // We only rely on the base Linux configuration of IMAFDC, so don't need ++ // dynamic feature detection. ++ method_ = kCpuInfoNone; ++#else ++#error Unrecognized target architecture ++#endif ++} ++ ++void CpuInfo::Cleanup() { ++ if (method_ == kCpuInfoCpuId) { ++ CpuId::Cleanup(); ++ } else if (method_ == kCpuInfoSystem) { ++ ProcCpuInfo::Cleanup(); ++ } else { ++ ASSERT(method_ == kCpuInfoNone); ++ } ++} ++ ++bool CpuInfo::FieldContains(CpuInfoIndices idx, const char* search_string) { ++ if (method_ == kCpuInfoCpuId) { ++ const char* field = CpuId::field(idx); ++ bool contains = (strstr(field, search_string) != NULL); ++ free(const_cast(field)); ++ return contains; ++ } else if (method_ == kCpuInfoSystem) { ++ return ProcCpuInfo::FieldContains(FieldName(idx), search_string); ++ } else { ++ UNREACHABLE(); ++ } ++} ++ ++const char* CpuInfo::ExtractField(CpuInfoIndices idx) { ++ if (method_ == kCpuInfoCpuId) { ++ return CpuId::field(idx); ++ } else if (method_ == kCpuInfoSystem) { ++ return ProcCpuInfo::ExtractField(FieldName(idx)); ++ } else { ++ UNREACHABLE(); ++ } ++} ++ ++bool CpuInfo::HasField(const char* field) { ++ if (method_ == kCpuInfoCpuId) { ++ return (strcmp(field, fields_[kCpuInfoProcessor]) == 0) || ++ (strcmp(field, fields_[kCpuInfoModel]) == 0) || ++ (strcmp(field, fields_[kCpuInfoHardware]) == 0) || ++ (strcmp(field, fields_[kCpuInfoFeatures]) == 0); ++ } else if (method_ == kCpuInfoSystem) { ++ return ProcCpuInfo::HasField(field); ++ } else if (method_ == kCpuInfoNone) { ++ return false; ++ } else { ++ UNREACHABLE(); ++ } ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc +index a0e900a85a3..9e022d1a470 100644 +--- a/runtime/vm/dart.cc ++++ b/runtime/vm/dart.cc +@@ -1205,6 +1205,8 @@ char* Dart::FeaturesString(IsolateGroup* isolate_group, + #endif + #elif defined(DART_TARGET_OS_LINUX) + buffer.AddString(" linux"); ++#elif defined(DART_TARGET_OS_OHOS) ++ buffer.AddString(" ohos"); + #elif defined(DART_TARGET_OS_WINDOWS) + buffer.AddString(" windows"); + #else +diff --git a/runtime/vm/globals.h b/runtime/vm/globals.h +index 80b830e821e..13db280340a 100644 +--- a/runtime/vm/globals.h ++++ b/runtime/vm/globals.h +@@ -115,7 +115,7 @@ const intptr_t kDefaultNewGenSemiMaxSize = (kWordSize <= 4) ? 8 : 16; + + #if defined(DART_ENABLE_TIMELINE) || !defined(PRODUCT) || \ + defined(DART_HOST_OS_FUCHSIA) || defined(DART_TARGET_OS_FUCHSIA) || \ +- defined(DART_TARGET_OS_ANDROID) ++ defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_OHOS) + #define SUPPORT_TIMELINE 1 + #endif + +diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc +index c11a5faf6fe..cc001b8f826 100644 +--- a/runtime/vm/image_snapshot.cc ++++ b/runtime/vm/image_snapshot.cc +@@ -1051,7 +1051,7 @@ class DwarfAssemblyStream : public DwarfWriteStream { + #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) + stream_->WriteString(".section __DWARF,__debug_abbrev,regular,debug\n"); + #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + stream_->WriteString(".section .debug_abbrev,\"\"\n"); + #else + UNIMPLEMENTED(); +@@ -1061,7 +1061,7 @@ class DwarfAssemblyStream : public DwarfWriteStream { + #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) + stream_->WriteString(".section __DWARF,__debug_info,regular,debug\n"); + #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + stream_->WriteString(".section .debug_info,\"\"\n"); + #else + UNIMPLEMENTED(); +@@ -1073,7 +1073,7 @@ class DwarfAssemblyStream : public DwarfWriteStream { + #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) + stream_->WriteString(".section __DWARF,__debug_line,regular,debug\n"); + #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + stream_->WriteString(".section .debug_line,\"\"\n"); + #else + UNIMPLEMENTED(); +@@ -1154,7 +1154,7 @@ void AssemblyImageWriter::Finalize() { + } + + #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + // Non-executable stack. + assembly_stream_->WriteString(".section .note.GNU-stack,\"\"\n"); + #endif +@@ -1312,7 +1312,7 @@ void AssemblyImageWriter::WriteROData(NonStreamingWriteStream* clustered_stream, + WriteBytes(bytes + last_position, symbol.offset - last_position); + assembly_stream_->Printf("\"%s\":\n", symbol.name); + #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + // Output size and type of the read-only data symbol to the assembly stream. + assembly_stream_->Printf(".size \"%s\", %zu\n", symbol.name, symbol.size); + assembly_stream_->Printf(".type \"%s\", %%object\n", symbol.name); +@@ -1354,7 +1354,7 @@ bool AssemblyImageWriter::EnterSection(ProgramSection section, + current_symbols_ = + new (zone_) ZoneGrowableArray(zone_, 0); + #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + assembly_stream_->WriteString(".section .rodata\n"); + #elif defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) + assembly_stream_->WriteString(".const\n"); +@@ -1411,7 +1411,7 @@ void AssemblyImageWriter::ExitSection(ProgramSection name, + // We should still be in the same section as the last EnterSection. + ASSERT_EQUAL(current_section_label_, SectionLabel(name, vm)); + #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + // Output the size of the section symbol to the assembly stream. + assembly_stream_->Printf(".size %s, %zu\n", SectionSymbol(name, vm), size); + assembly_stream_->Printf(".type %s, %%object\n", SectionSymbol(name, vm)); +@@ -1505,7 +1505,7 @@ void AssemblyImageWriter::AddCodeSymbol(const Code& code, + } + assembly_stream_->Printf("\"%s\":\n", symbol); + #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + // Output the size of the code symbol to the assembly stream. + assembly_stream_->Printf(".size \"%s\", %zu\n", symbol, code.Size()); + assembly_stream_->Printf(".type \"%s\", %%function\n", symbol); +diff --git a/runtime/vm/native_symbol_ohos.cc b/runtime/vm/native_symbol_ohos.cc +new file mode 100644 +index 00000000000..37c9e47402d +--- /dev/null ++++ b/runtime/vm/native_symbol_ohos.cc +@@ -0,0 +1,72 @@ ++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "vm/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "platform/memory_sanitizer.h" ++#include "vm/native_symbol.h" ++#include "vm/os.h" ++ ++#include // NOLINT ++#include // NOLINT ++ ++namespace dart { ++ ++void NativeSymbolResolver::Init() {} ++ ++void NativeSymbolResolver::Cleanup() {} ++ ++char* NativeSymbolResolver::LookupSymbolName(uword pc, uword* start) { ++ Dl_info info; ++ int r = dladdr(reinterpret_cast(pc), &info); ++ if (r == 0) { ++ return NULL; ++ } ++ if (info.dli_sname == NULL) { ++ return NULL; ++ } ++ if (start != NULL) { ++ *start = reinterpret_cast(info.dli_saddr); ++ } ++ int status = 0; ++ size_t len = 0; ++ char* demangled = abi::__cxa_demangle(info.dli_sname, NULL, &len, &status); ++ MSAN_UNPOISON(demangled, len); ++ if (status == 0) { ++ return demangled; ++ } ++ return strdup(info.dli_sname); ++} ++ ++void NativeSymbolResolver::FreeSymbolName(char* name) { ++ free(name); ++} ++ ++bool NativeSymbolResolver::LookupSharedObject(uword pc, ++ uword* dso_base, ++ char** dso_name) { ++ Dl_info info; ++ int r = dladdr(reinterpret_cast(pc), &info); ++ if (r == 0) { ++ return false; ++ } ++ if (dso_base != nullptr) { ++ *dso_base = reinterpret_cast(info.dli_fbase); ++ } ++ if (dso_name != nullptr) { ++ *dso_name = strdup(info.dli_fname); ++ } ++ return true; ++} ++ ++void NativeSymbolResolver::AddSymbols(const char* dso_name, ++ void* buffer, ++ size_t size) { ++ OS::PrintErr("warning: Dart_AddSymbols has no effect on Ohos\n"); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/os_ohos.cc b/runtime/vm/os_ohos.cc +new file mode 100644 +index 00000000000..7ac0322c01c +--- /dev/null ++++ b/runtime/vm/os_ohos.cc +@@ -0,0 +1,669 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "vm/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "vm/os.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "platform/memory_sanitizer.h" ++#include "platform/utils.h" ++#include "vm/code_comments.h" ++#include "vm/code_observers.h" ++#include "vm/dart.h" ++#include "vm/flags.h" ++#include "vm/isolate.h" ++#include "vm/lockers.h" ++#include "vm/os_thread.h" ++#include "vm/timeline.h" ++#include "vm/zone.h" ++ ++namespace dart { ++ ++#ifndef PRODUCT ++ ++DEFINE_FLAG(bool, ++ generate_perf_events_symbols, ++ false, ++ "Generate events symbols for profiling with perf (disables dual " ++ "code mapping)"); ++ ++DEFINE_FLAG(bool, ++ generate_perf_jitdump, ++ false, ++ "Generate jitdump file to use with perf-inject (disables dual code " ++ "mapping)"); ++ ++DECLARE_FLAG(bool, write_protect_code); ++DECLARE_FLAG(bool, write_protect_vm_isolate); ++#if !defined(DART_PRECOMPILED_RUNTIME) ++DECLARE_FLAG(bool, code_comments); ++#endif ++ ++// Linux CodeObservers. ++ ++// Simple perf support: generate /tmp/perf-.map file that maps ++// memory ranges to symbol names for JIT generated code. This allows ++// perf-report to resolve addresses falling into JIT generated code. ++// However perf-annotate does not work in this mode because JIT code ++// is transient and does not exist anymore at the moment when you ++// invoke perf-report. ++class PerfCodeObserver : public CodeObserver { ++ public: ++ PerfCodeObserver() : out_file_(NULL) { ++ Dart_FileOpenCallback file_open = Dart::file_open_callback(); ++ if (file_open == NULL) { ++ return; ++ } ++ intptr_t pid = getpid(); ++ char* filename = OS::SCreate(NULL, "/tmp/perf-%" Pd ".map", pid); ++ out_file_ = (*file_open)(filename, true); ++ free(filename); ++ } ++ ++ ~PerfCodeObserver() { ++ Dart_FileCloseCallback file_close = Dart::file_close_callback(); ++ if ((file_close == NULL) || (out_file_ == NULL)) { ++ return; ++ } ++ (*file_close)(out_file_); ++ } ++ ++ virtual bool IsActive() const { ++ return FLAG_generate_perf_events_symbols && (out_file_ != NULL); ++ } ++ ++ virtual void Notify(const char* name, ++ uword base, ++ uword prologue_offset, ++ uword size, ++ bool optimized, ++ const CodeComments* comments) { ++ Dart_FileWriteCallback file_write = Dart::file_write_callback(); ++ if ((file_write == NULL) || (out_file_ == NULL)) { ++ return; ++ } ++ const char* marker = optimized ? "*" : ""; ++ char* buffer = ++ OS::SCreate(Thread::Current()->zone(), "%" Px " %" Px " %s%s\n", base, ++ size, marker, name); ++ { ++ MutexLocker ml(CodeObservers::mutex()); ++ (*file_write)(buffer, strlen(buffer), out_file_); ++ } ++ } ++ ++ private: ++ void* out_file_; ++ ++ DISALLOW_COPY_AND_ASSIGN(PerfCodeObserver); ++}; ++ ++// Code observer that generates a JITDUMP[1] file that can be interpreted by ++// perf-inject to generate ELF images for JIT generated code objects, which ++// allows both perf-report and perf-annotate to recognize them. ++// ++// Usage: ++// ++// $ perf record -k mono dart --generate-perf-jitdump benchmark.dart ++// $ perf inject -j -i perf.data -o perf.data.jitted ++// $ perf report -i perf.data.jitted ++// ++// [1] see linux/tools/perf/Documentation/jitdump-specification.txt for ++// JITDUMP binary format. ++class JitDumpCodeObserver : public CodeObserver { ++ public: ++ JitDumpCodeObserver() : pid_(getpid()) { ++ char* const filename = OS::SCreate(nullptr, "/tmp/jit-%" Pd ".dump", pid_); ++ const int fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0666); ++ free(filename); ++ ++ if (fd == -1) { ++ return; ++ } ++ ++ // Map JITDUMP file, this mapping will be recorded by perf. This allows ++ // perf-inject to find this file later. ++ const long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int) ++ if (page_size == -1) { ++ close(fd); ++ return; ++ } ++ ++ mapped_ = ++ mmap(nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0); ++ if (mapped_ == nullptr) { ++ close(fd); ++ return; ++ } ++ mapped_size_ = page_size; ++ ++ out_file_ = fdopen(fd, "w+"); ++ if (out_file_ == nullptr) { ++ close(fd); ++ return; ++ } ++ ++ // Buffer the output to avoid high IO overheads - we are going to be ++ // writing all JIT generated code out. ++ setvbuf(out_file_, nullptr, _IOFBF, 2 * MB); ++ ++ // Disable code write protection and vm isolate write protection, because ++ // calling mprotect on the pages filled with JIT generated code objects ++ // confuses perf. ++ FLAG_write_protect_code = false; ++ FLAG_write_protect_vm_isolate = false; ++ ++#if !defined(DART_PRECOMPILED_RUNTIME) ++ // Enable code comments. ++ FLAG_code_comments = true; ++#endif ++ ++ // Write JITDUMP header. ++ WriteHeader(); ++ } ++ ++ ~JitDumpCodeObserver() { ++ if (mapped_ != nullptr) { ++ munmap(mapped_, mapped_size_); ++ mapped_ = nullptr; ++ } ++ ++ if (out_file_ != nullptr) { ++ fclose(out_file_); ++ out_file_ = nullptr; ++ } ++ } ++ ++ virtual bool IsActive() const { ++ return FLAG_generate_perf_jitdump && (out_file_ != nullptr); ++ } ++ ++ virtual void Notify(const char* name, ++ uword base, ++ uword prologue_offset, ++ uword size, ++ bool optimized, ++ const CodeComments* comments) { ++ MutexLocker ml(CodeObservers::mutex()); ++ ++ const char* marker = optimized ? "*" : ""; ++ char* buffer = OS::SCreate(Thread::Current()->zone(), "%s%s", marker, name); ++ const size_t name_length = strlen(buffer); ++ ++ WriteDebugInfo(base, comments); ++ ++ CodeLoadEvent ev; ++ ev.event = BaseEvent::kLoad; ++ ev.size = sizeof(ev) + (name_length + 1) + size; ++ ev.time_stamp = OS::GetCurrentMonotonicTicks(); ++ ev.process_id = getpid(); ++ ev.thread_id = syscall(SYS_gettid); ++ ev.vma = base; ++ ev.code_address = base; ++ ev.code_size = size; ++ ev.code_id = code_id_++; ++ ++ WriteFully(&ev, sizeof(ev)); ++ WriteFully(buffer, name_length + 1); ++ WriteFully(reinterpret_cast(base), size); ++ } ++ ++ private: ++ struct Header { ++ const uint32_t magic = 0x4A695444; ++ const uint32_t version = 1; ++ const uint32_t size = sizeof(Header); ++ uint32_t elf_mach_target; ++ const uint32_t reserved = 0xDEADBEEF; ++ uint32_t process_id; ++ uint64_t time_stamp; ++ const uint64_t flags = 0; ++ }; ++ ++ struct BaseEvent { ++ enum Event { ++ kLoad = 0, ++ kMove = 1, ++ kDebugInfo = 2, ++ kClose = 3, ++ kUnwindingInfo = 4 ++ }; ++ ++ uint32_t event; ++ uint32_t size; ++ uint64_t time_stamp; ++ }; ++ ++ struct CodeLoadEvent : BaseEvent { ++ uint32_t process_id; ++ uint32_t thread_id; ++ uint64_t vma; ++ uint64_t code_address; ++ uint64_t code_size; ++ uint64_t code_id; ++ }; ++ ++ struct DebugInfoEvent : BaseEvent { ++ uint64_t address; ++ uint64_t entry_count; ++ // DebugInfoEntry entries[entry_count_]; ++ }; ++ ++ struct DebugInfoEntry { ++ uint64_t address; ++ int32_t line_number; ++ int32_t column; ++ // Followed by nul-terminated name. ++ }; ++ ++ // ELF machine architectures ++ // From linux/include/uapi/linux/elf-em.h ++ static const uint32_t EM_386 = 3; ++ static const uint32_t EM_X86_64 = 62; ++ static const uint32_t EM_ARM = 40; ++ static const uint32_t EM_AARCH64 = 183; ++ static const uint32_t EM_RISCV = 243; ++ ++ static uint32_t GetElfMachineArchitecture() { ++#if TARGET_ARCH_IA32 ++ return EM_386; ++#elif TARGET_ARCH_X64 ++ return EM_X86_64; ++#elif TARGET_ARCH_ARM ++ return EM_ARM; ++#elif TARGET_ARCH_ARM64 ++ return EM_AARCH64; ++#elif TARGET_ARCH_RISCV32 || TARGET_ARCH_RISCV64 ++ return EM_RISCV; ++#else ++ UNREACHABLE(); ++ return 0; ++#endif ++ } ++ ++#if ARCH_IS_64_BIT ++ static const int kElfHeaderSize = 0x40; ++#else ++ static const int kElfHeaderSize = 0x34; ++#endif ++ ++ void WriteDebugInfo(uword base, const CodeComments* comments) { ++ if (comments == nullptr || comments->Length() == 0) { ++ return; ++ } ++ ++ // Open the comments file for the given code object. ++ // Note: for some reason we can't emit all comments into a single file ++ // the mapping between PCs and lines goes out of sync (might be ++ // perf-annotate bug). ++ char* comments_file_name = ++ OS::SCreate(nullptr, "/tmp/jit-%" Pd "-%" Pd ".cmts", pid_, code_id_); ++ const intptr_t filename_length = strlen(comments_file_name); ++ FILE* comments_file = fopen(comments_file_name, "w"); ++ setvbuf(comments_file, nullptr, _IOFBF, 2 * MB); ++ ++ // Count the number of DebugInfoEntry we are going to emit: one ++ // per PC. ++ intptr_t entry_count = 0; ++ for (uint64_t i = 0, len = comments->Length(); i < len;) { ++ const intptr_t pc_offset = comments->PCOffsetAt(i); ++ while (i < len && comments->PCOffsetAt(i) == pc_offset) { ++ i++; ++ } ++ entry_count++; ++ } ++ ++ DebugInfoEvent info; ++ info.event = BaseEvent::kDebugInfo; ++ info.time_stamp = OS::GetCurrentMonotonicTicks(); ++ info.address = base; ++ info.entry_count = entry_count; ++ info.size = sizeof(info) + ++ entry_count * (sizeof(DebugInfoEntry) + filename_length + 1); ++ const int32_t padding = Utils::RoundUp(info.size, 8) - info.size; ++ info.size += padding; ++ ++ // Write out DebugInfoEvent record followed by entry_count DebugInfoEntry ++ // records. ++ WriteFully(&info, sizeof(info)); ++ intptr_t line_number = 0; // Line number within comments_file. ++ for (intptr_t i = 0, len = comments->Length(); i < len;) { ++ const intptr_t pc_offset = comments->PCOffsetAt(i); ++ while (i < len && comments->PCOffsetAt(i) == pc_offset) { ++ line_number += WriteLn(comments_file, comments->CommentAt(i)); ++ i++; ++ } ++ DebugInfoEntry entry; ++ entry.address = base + pc_offset + kElfHeaderSize; ++ entry.line_number = line_number; ++ entry.column = 0; ++ WriteFully(&entry, sizeof(entry)); ++ WriteFully(comments_file_name, filename_length + 1); ++ } ++ ++ // Write out the padding. ++ const char padding_bytes[8] = {0}; ++ WriteFully(padding_bytes, padding); ++ ++ fclose(comments_file); ++ free(comments_file_name); ++ } ++ ++ void WriteHeader() { ++ Header header; ++ header.elf_mach_target = GetElfMachineArchitecture(); ++ header.process_id = getpid(); ++ header.time_stamp = OS::GetCurrentTimeMicros(); ++ WriteFully(&header, sizeof(header)); ++ } ++ ++ // Returns number of new-lines written. ++ intptr_t WriteLn(FILE* f, const char* comment) { ++ fputs(comment, f); ++ fputc('\n', f); ++ ++ intptr_t line_count = 1; ++ while ((comment = strstr(comment, "\n")) != nullptr) { ++ line_count++; ++ } ++ return line_count; ++ } ++ ++ void WriteFully(const void* buffer, size_t size) { ++ const char* ptr = static_cast(buffer); ++ while (size > 0) { ++ const size_t written = fwrite(ptr, 1, size, out_file_); ++ if (written == 0) { ++ UNREACHABLE(); ++ break; ++ } ++ size -= written; ++ ptr += written; ++ } ++ } ++ ++ const intptr_t pid_; ++ ++ FILE* out_file_ = nullptr; ++ void* mapped_ = nullptr; ++ long mapped_size_ = 0; // NOLINT(runtime/int) ++ ++ intptr_t code_id_ = 0; ++ ++ DISALLOW_COPY_AND_ASSIGN(JitDumpCodeObserver); ++}; ++ ++#endif // !PRODUCT ++ ++intptr_t OS::ProcessId() { ++ return static_cast(getpid()); ++} ++ ++static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) { ++ time_t seconds = static_cast(seconds_since_epoch); ++ if (seconds != seconds_since_epoch) return false; ++ struct tm* error_code = localtime_r(&seconds, tm_result); ++ return error_code != NULL; ++} ++ ++const char* OS::GetTimeZoneName(int64_t seconds_since_epoch) { ++ tm decomposed; ++ bool succeeded = LocalTime(seconds_since_epoch, &decomposed); ++ // If unsuccessful, return an empty string like V8 does. ++ return (succeeded && (decomposed.tm_zone != NULL)) ? decomposed.tm_zone : ""; ++} ++ ++int OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch) { ++ tm decomposed; ++ bool succeeded = LocalTime(seconds_since_epoch, &decomposed); ++ // Even if the offset was 24 hours it would still easily fit into 32 bits. ++ // If unsuccessful, return zero like V8 does. ++ return succeeded ? static_cast(decomposed.tm_gmtoff) : 0; ++} ++ ++int64_t OS::GetCurrentTimeMillis() { ++ return GetCurrentTimeMicros() / 1000; ++} ++ ++int64_t OS::GetCurrentTimeMicros() { ++ // gettimeofday has microsecond resolution. ++ struct timeval tv; ++ if (gettimeofday(&tv, NULL) < 0) { ++ UNREACHABLE(); ++ return 0; ++ } ++ return (static_cast(tv.tv_sec) * 1000000) + tv.tv_usec; ++} ++ ++int64_t OS::GetCurrentMonotonicTicks() { ++ struct timespec ts; ++ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { ++ UNREACHABLE(); ++ return 0; ++ } ++ // Convert to nanoseconds. ++ int64_t result = ts.tv_sec; ++ result *= kNanosecondsPerSecond; ++ result += ts.tv_nsec; ++ return result; ++} ++ ++int64_t OS::GetCurrentMonotonicFrequency() { ++ return kNanosecondsPerSecond; ++} ++ ++int64_t OS::GetCurrentMonotonicMicros() { ++ int64_t ticks = GetCurrentMonotonicTicks(); ++ ASSERT(GetCurrentMonotonicFrequency() == kNanosecondsPerSecond); ++ return ticks / kNanosecondsPerMicrosecond; ++} ++ ++int64_t OS::GetCurrentThreadCPUMicros() { ++ struct timespec ts; ++ if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != 0) { ++ UNREACHABLE(); ++ return -1; ++ } ++ int64_t result = ts.tv_sec; ++ result *= kMicrosecondsPerSecond; ++ result += (ts.tv_nsec / kNanosecondsPerMicrosecond); ++ return result; ++} ++ ++int64_t OS::GetCurrentMonotonicMicrosForTimeline() { ++#if defined(SUPPORT_TIMELINE) ++ if (Timeline::recorder_discards_clock_values()) return -1; ++ return GetCurrentMonotonicMicros(); ++#else ++ return -1; ++#endif ++} ++ ++int64_t OS::GetCurrentThreadCPUMicrosForTimeline() { ++#if defined(SUPPORT_TIMELINE) ++ if (Timeline::recorder_discards_clock_values()) return -1; ++ return OS::GetCurrentThreadCPUMicros(); ++#else ++ return -1; ++#endif ++} ++// TODO(5411554): May need to hoist these architecture dependent code ++// into a architecture specific file e.g: os_ia32_linux.cc ++intptr_t OS::ActivationFrameAlignment() { ++#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) || \ ++ defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_RISCV32) || \ ++ defined(TARGET_ARCH_RISCV64) ++ const int kMinimumAlignment = 16; ++#elif defined(TARGET_ARCH_ARM) ++ const int kMinimumAlignment = 8; ++#else ++#error Unsupported architecture. ++#endif ++ intptr_t alignment = kMinimumAlignment; ++ // TODO(5411554): Allow overriding default stack alignment for ++ // testing purposes. ++ // Flags::DebugIsInt("stackalign", &alignment); ++ ASSERT(Utils::IsPowerOfTwo(alignment)); ++ ASSERT(alignment >= kMinimumAlignment); ++ return alignment; ++} ++ ++int OS::NumberOfAvailableProcessors() { ++ return sysconf(_SC_NPROCESSORS_ONLN); ++} ++ ++void OS::Sleep(int64_t millis) { ++ int64_t micros = millis * kMicrosecondsPerMillisecond; ++ SleepMicros(micros); ++} ++ ++void OS::SleepMicros(int64_t micros) { ++ struct timespec req; // requested. ++ struct timespec rem; // remainder. ++ int64_t seconds = micros / kMicrosecondsPerSecond; ++ micros = micros - seconds * kMicrosecondsPerSecond; ++ int64_t nanos = micros * kNanosecondsPerMicrosecond; ++ req.tv_sec = seconds; ++ req.tv_nsec = nanos; ++ while (true) { ++ int r = nanosleep(&req, &rem); ++ if (r == 0) { ++ break; ++ } ++ // We should only ever see an interrupt error. ++ ASSERT(errno == EINTR); ++ // Copy remainder into requested and repeat. ++ req = rem; ++ } ++} ++ ++// TODO(regis): Function called only from the simulator. ++void OS::DebugBreak() { ++ __builtin_trap(); ++} ++ ++DART_NOINLINE uintptr_t OS::GetProgramCounter() { ++ return reinterpret_cast( ++ __builtin_extract_return_addr(__builtin_return_address(0))); ++} ++ ++void OS::Print(const char* format, ...) { ++ va_list args; ++ va_start(args, format); ++ VFPrint(stdout, format, args); ++ va_end(args); ++} ++ ++void OS::VFPrint(FILE* stream, const char* format, va_list args) { ++ vfprintf(stream, format, args); ++ fflush(stream); ++} ++ ++char* OS::SCreate(Zone* zone, const char* format, ...) { ++ va_list args; ++ va_start(args, format); ++ char* buffer = VSCreate(zone, format, args); ++ va_end(args); ++ return buffer; ++} ++ ++char* OS::VSCreate(Zone* zone, const char* format, va_list args) { ++ // Measure. ++ va_list measure_args; ++ va_copy(measure_args, args); ++ intptr_t len = Utils::VSNPrint(NULL, 0, format, measure_args); ++ va_end(measure_args); ++ ++ char* buffer; ++ if (zone != nullptr) { ++ buffer = zone->Alloc(len + 1); ++ } else { ++ buffer = reinterpret_cast(malloc(len + 1)); ++ } ++ ASSERT(buffer != NULL); ++ ++ // Print. ++ va_list print_args; ++ va_copy(print_args, args); ++ Utils::VSNPrint(buffer, len + 1, format, print_args); ++ va_end(print_args); ++ return buffer; ++} ++ ++bool OS::StringToInt64(const char* str, int64_t* value) { ++ ASSERT(str != NULL && strlen(str) > 0 && value != NULL); ++ int32_t base = 10; ++ char* endptr; ++ int i = 0; ++ if (str[0] == '-') { ++ i = 1; ++ } else if (str[0] == '+') { ++ i = 1; ++ } ++ if ((str[i] == '0') && (str[i + 1] == 'x' || str[i + 1] == 'X') && ++ (str[i + 2] != '\0')) { ++ base = 16; ++ } ++ errno = 0; ++ if (base == 16) { ++ // Unsigned 64-bit hexadecimal integer literals are allowed but ++ // immediately interpreted as signed 64-bit integers. ++ *value = static_cast(strtoull(str, &endptr, base)); ++ } else { ++ *value = strtoll(str, &endptr, base); ++ } ++ return ((errno == 0) && (endptr != str) && (*endptr == 0)); ++} ++ ++void OS::RegisterCodeObservers() { ++#ifndef PRODUCT ++ if (FLAG_generate_perf_events_symbols) { ++ CodeObservers::Register(new PerfCodeObserver); ++ } ++ ++ if (FLAG_generate_perf_jitdump) { ++ CodeObservers::Register(new JitDumpCodeObserver); ++ } ++#endif // !PRODUCT ++} ++ ++void OS::PrintErr(const char* format, ...) { ++ va_list args; ++ va_start(args, format); ++ VFPrint(stderr, format, args); ++ va_end(args); ++} ++ ++void OS::Init() {} ++ ++void OS::Cleanup() {} ++ ++void OS::PrepareToAbort() {} ++ ++void OS::Abort() { ++ PrepareToAbort(); ++ abort(); ++} ++ ++void OS::Exit(int code) { ++ exit(code); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/os_thread_ohos.cc b/runtime/vm/os_thread_ohos.cc +new file mode 100644 +index 00000000000..e92a8c26794 +--- /dev/null ++++ b/runtime/vm/os_thread_ohos.cc +@@ -0,0 +1,500 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" // NOLINT ++ ++#if defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL) ++ ++#include "vm/os_thread.h" ++ ++#include // NOLINT ++#include ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "platform/address_sanitizer.h" ++#include "platform/assert.h" ++#include "platform/safe_stack.h" ++#include "platform/signal_blocker.h" ++#include "platform/utils.h" ++ ++#include "vm/flags.h" ++ ++namespace dart { ++ ++DEFINE_FLAG(int, ++ worker_thread_priority, ++ kMinInt, ++ "The thread priority the VM should use for new worker threads."); ++ ++#define VALIDATE_PTHREAD_RESULT(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ FATAL2("pthread error: %d (%s)", result, \ ++ Utils::StrError(result, error_buf, kBufferSize)); \ ++ } ++ ++// Variation of VALIDATE_PTHREAD_RESULT for named objects. ++#if defined(PRODUCT) ++#define VALIDATE_PTHREAD_RESULT_NAMED(result) VALIDATE_PTHREAD_RESULT(result) ++#else ++#define VALIDATE_PTHREAD_RESULT_NAMED(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ FATAL3("[%s] pthread error: %d (%s)", name_, result, \ ++ Utils::StrError(result, error_buf, kBufferSize)); \ ++ } ++#endif ++ ++#if defined(DEBUG) ++#define ASSERT_PTHREAD_SUCCESS(result) VALIDATE_PTHREAD_RESULT(result) ++#else ++// NOTE: This (currently) expands to a no-op. ++#define ASSERT_PTHREAD_SUCCESS(result) ASSERT(result == 0) ++#endif ++ ++#ifdef DEBUG ++#define RETURN_ON_PTHREAD_FAILURE(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ fprintf(stderr, "%s:%d: pthread error: %d (%s)\n", __FILE__, __LINE__, \ ++ result, Utils::StrError(result, error_buf, kBufferSize)); \ ++ return result; \ ++ } ++#else ++#define RETURN_ON_PTHREAD_FAILURE(result) \ ++ if (result != 0) return result; ++#endif ++ ++static void ComputeTimeSpecMicros(struct timespec* ts, int64_t micros) { ++ int64_t secs = micros / kMicrosecondsPerSecond; ++ int64_t nanos = ++ (micros - (secs * kMicrosecondsPerSecond)) * kNanosecondsPerMicrosecond; ++ int result = clock_gettime(CLOCK_MONOTONIC, ts); ++ ASSERT(result == 0); ++ ts->tv_sec += secs; ++ ts->tv_nsec += nanos; ++ if (ts->tv_nsec >= kNanosecondsPerSecond) { ++ ts->tv_sec += 1; ++ ts->tv_nsec -= kNanosecondsPerSecond; ++ } ++} ++ ++class ThreadStartData { ++ public: ++ ThreadStartData(const char* name, ++ OSThread::ThreadStartFunction function, ++ uword parameter) ++ : name_(name), function_(function), parameter_(parameter) {} ++ ++ const char* name() const { return name_; } ++ OSThread::ThreadStartFunction function() const { return function_; } ++ uword parameter() const { return parameter_; } ++ ++ private: ++ const char* name_; ++ OSThread::ThreadStartFunction function_; ++ uword parameter_; ++ ++ DISALLOW_COPY_AND_ASSIGN(ThreadStartData); ++}; ++ ++// TODO(bkonyi): remove this call once the prebuilt SDK is updated. ++// Spawned threads inherit their spawner's signal mask. We sometimes spawn ++// threads for running Dart code from a thread that is blocking SIGPROF. ++// This function explicitly unblocks SIGPROF so the profiler continues to ++// sample this thread. ++static void UnblockSIGPROF() { ++ sigset_t set; ++ sigemptyset(&set); ++ sigaddset(&set, SIGPROF); ++ int r = pthread_sigmask(SIG_UNBLOCK, &set, NULL); ++ USE(r); ++ ASSERT(r == 0); ++ ASSERT(!CHECK_IS_BLOCKING(SIGPROF)); ++} ++ ++// Dispatch to the thread start function provided by the caller. This trampoline ++// is used to ensure that the thread is properly destroyed if the thread just ++// exits. ++static void* ThreadStart(void* data_ptr) { ++ if (FLAG_worker_thread_priority != kMinInt) { ++ if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), ++ FLAG_worker_thread_priority) == -1) { ++ FATAL2("Setting thread priority to %d failed: errno = %d\n", ++ FLAG_worker_thread_priority, errno); ++ } ++ } ++ ++ ThreadStartData* data = reinterpret_cast(data_ptr); ++ ++ const char* name = data->name(); ++ OSThread::ThreadStartFunction function = data->function(); ++ uword parameter = data->parameter(); ++ delete data; ++ ++ // Set the thread name. There is 16 bytes limit on the name (including \0). ++ // pthread_setname_np ignores names that are too long rather than truncating. ++ char truncated_name[16]; ++ snprintf(truncated_name, ARRAY_SIZE(truncated_name), "%s", name); ++ pthread_setname_np(pthread_self(), truncated_name); ++ ++ // Create new OSThread object and set as TLS for new thread. ++ OSThread* thread = OSThread::CreateOSThread(); ++ if (thread != NULL) { ++ OSThread::SetCurrent(thread); ++ thread->set_name(name); ++ UnblockSIGPROF(); ++ // Call the supplied thread start function handing it its parameters. ++ function(parameter); ++ } ++ ++ return NULL; ++} ++ ++int OSThread::Start(const char* name, ++ ThreadStartFunction function, ++ uword parameter) { ++ pthread_attr_t attr; ++ int result = pthread_attr_init(&attr); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_setstacksize(&attr, OSThread::GetMaxStackSize()); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ ThreadStartData* data = new ThreadStartData(name, function, parameter); ++ ++ pthread_t tid; ++ result = pthread_create(&tid, &attr, ThreadStart, data); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_destroy(&attr); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ return 0; ++} ++ ++const ThreadId OSThread::kInvalidThreadId = static_cast(0); ++const ThreadJoinId OSThread::kInvalidThreadJoinId = ++ static_cast(0); ++ ++ThreadLocalKey OSThread::CreateThreadLocal(ThreadDestructor destructor) { ++ pthread_key_t key = kUnsetThreadLocalKey; ++ int result = pthread_key_create(&key, destructor); ++ VALIDATE_PTHREAD_RESULT(result); ++ ASSERT(key != kUnsetThreadLocalKey); ++ return key; ++} ++ ++void OSThread::DeleteThreadLocal(ThreadLocalKey key) { ++ ASSERT(key != kUnsetThreadLocalKey); ++ int result = pthread_key_delete(key); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void OSThread::SetThreadLocal(ThreadLocalKey key, uword value) { ++ ASSERT(key != kUnsetThreadLocalKey); ++ int result = pthread_setspecific(key, reinterpret_cast(value)); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++intptr_t OSThread::GetMaxStackSize() { ++ const int kStackSize = (128 * kWordSize * KB); ++ return kStackSize; ++} ++ ++ThreadId OSThread::GetCurrentThreadId() { ++ return pthread_self(); ++} ++ ++#ifdef SUPPORT_TIMELINE ++ThreadId OSThread::GetCurrentThreadTraceId() { ++ return syscall(__NR_gettid); ++} ++#endif // SUPPORT_TIMELINE ++ ++ThreadJoinId OSThread::GetCurrentThreadJoinId(OSThread* thread) { ++ ASSERT(thread != NULL); ++ // Make sure we're filling in the join id for the current thread. ++ ASSERT(thread->id() == GetCurrentThreadId()); ++ // Make sure the join_id_ hasn't been set, yet. ++ DEBUG_ASSERT(thread->join_id_ == kInvalidThreadJoinId); ++ pthread_t id = pthread_self(); ++#if defined(DEBUG) ++ thread->join_id_ = id; ++#endif ++ return id; ++} ++ ++void OSThread::Join(ThreadJoinId id) { ++ int result = pthread_join(id, NULL); ++ ASSERT(result == 0); ++} ++ ++intptr_t OSThread::ThreadIdToIntPtr(ThreadId id) { ++ ASSERT(sizeof(id) == sizeof(intptr_t)); ++ return static_cast(id); ++} ++ ++ThreadId OSThread::ThreadIdFromIntPtr(intptr_t id) { ++ return static_cast(id); ++} ++ ++bool OSThread::Compare(ThreadId a, ThreadId b) { ++ return pthread_equal(a, b) != 0; ++} ++ ++bool OSThread::GetCurrentStackBounds(uword* lower, uword* upper) { ++ pthread_attr_t attr; ++ // May fail on the main thread. ++ if (pthread_getattr_np(pthread_self(), &attr) != 0) { ++ return false; ++ } ++ ++ void* base; ++ size_t size; ++ int error = pthread_attr_getstack(&attr, &base, &size); ++ pthread_attr_destroy(&attr); ++ if (error != 0) { ++ return false; ++ } ++ ++ *lower = reinterpret_cast(base); ++ *upper = *lower + size; ++ return true; ++} ++ ++#if defined(USING_SAFE_STACK) ++NO_SANITIZE_ADDRESS ++NO_SANITIZE_SAFE_STACK ++uword OSThread::GetCurrentSafestackPointer() { ++#error "SAFE_STACK is unsupported on this platform" ++ return 0; ++} ++ ++NO_SANITIZE_ADDRESS ++NO_SANITIZE_SAFE_STACK ++void OSThread::SetCurrentSafestackPointer(uword ssp) { ++#error "SAFE_STACK is unsupported on this platform" ++} ++#endif ++ ++Mutex::Mutex(NOT_IN_PRODUCT(const char* name)) ++#if !defined(PRODUCT) ++ : name_(name) ++#endif ++{ ++ pthread_mutexattr_t attr; ++ int result = pthread_mutexattr_init(&attr); ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++ ++#if defined(DEBUG) ++ result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++#endif // defined(DEBUG) ++ ++ result = pthread_mutex_init(data_.mutex(), &attr); ++ // Verify that creating a pthread_mutex succeeded. ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++ ++ result = pthread_mutexattr_destroy(&attr); ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++} ++ ++Mutex::~Mutex() { ++ int result = pthread_mutex_destroy(data_.mutex()); ++ // Verify that the pthread_mutex was destroyed. ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++#endif // defined(DEBUG) ++} ++ ++void Mutex::Lock() { ++ int result = pthread_mutex_lock(data_.mutex()); ++ // Specifically check for dead lock to help debugging. ++ ASSERT(result != EDEADLK); ++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors. ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ owner_ = OSThread::GetCurrentThreadId(); ++#endif // defined(DEBUG) ++} ++ ++bool Mutex::TryLock() { ++ int result = pthread_mutex_trylock(data_.mutex()); ++ // Return false if the lock is busy and locking failed. ++ if (result == EBUSY) { ++ return false; ++ } ++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors. ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ owner_ = OSThread::GetCurrentThreadId(); ++#endif // defined(DEBUG) ++ return true; ++} ++ ++void Mutex::Unlock() { ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++ int result = pthread_mutex_unlock(data_.mutex()); ++ // Specifically check for wrong thread unlocking to aid debugging. ++ ASSERT(result != EPERM); ++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors. ++} ++ ++Monitor::Monitor() { ++ pthread_mutexattr_t mutex_attr; ++ int result = pthread_mutexattr_init(&mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ result = pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK); ++ VALIDATE_PTHREAD_RESULT(result); ++#endif // defined(DEBUG) ++ ++ result = pthread_mutex_init(data_.mutex(), &mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_mutexattr_destroy(&mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ pthread_condattr_t cond_attr; ++ result = pthread_condattr_init(&cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_cond_init(data_.cond(), &cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_condattr_destroy(&cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++} ++ ++Monitor::~Monitor() { ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++#endif // defined(DEBUG) ++ ++ int result = pthread_mutex_destroy(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_cond_destroy(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++bool Monitor::TryEnter() { ++ int result = pthread_mutex_trylock(data_.mutex()); ++ // Return false if the lock is busy and locking failed. ++ if (result == EBUSY) { ++ return false; ++ } ++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors. ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++ owner_ = OSThread::GetCurrentThreadId(); ++#endif // defined(DEBUG) ++ return true; ++} ++ ++void Monitor::Enter() { ++ int result = pthread_mutex_lock(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++ owner_ = OSThread::GetCurrentThreadId(); ++#endif // defined(DEBUG) ++} ++ ++void Monitor::Exit() { ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++ ++ int result = pthread_mutex_unlock(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++Monitor::WaitResult Monitor::Wait(int64_t millis) { ++ Monitor::WaitResult retval = WaitMicros(millis * kMicrosecondsPerMillisecond); ++ return retval; ++} ++ ++Monitor::WaitResult Monitor::WaitMicros(int64_t micros) { ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ ThreadId saved_owner = owner_; ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++ ++ Monitor::WaitResult retval = kNotified; ++ if (micros == kNoTimeout) { ++ // Wait forever. ++ int result = pthread_cond_wait(data_.cond(), data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ } else { ++ struct timespec ts; ++ ComputeTimeSpecMicros(&ts, micros); ++ int result = pthread_cond_timedwait(data_.cond(), data_.mutex(), &ts); ++ ASSERT((result == 0) || (result == ETIMEDOUT)); ++ if (result == ETIMEDOUT) { ++ retval = kTimedOut; ++ } ++ } ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++ owner_ = OSThread::GetCurrentThreadId(); ++ ASSERT(owner_ == saved_owner); ++#endif // defined(DEBUG) ++ return retval; ++} ++ ++void Monitor::Notify() { ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ int result = pthread_cond_signal(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void Monitor::NotifyAll() { ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ int result = pthread_cond_broadcast(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL) +diff --git a/runtime/vm/os_thread_ohos.h b/runtime/vm/os_thread_ohos.h +new file mode 100644 +index 00000000000..aaf490f9f77 +--- /dev/null ++++ b/runtime/vm/os_thread_ohos.h +@@ -0,0 +1,76 @@ ++// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#ifndef RUNTIME_VM_OS_THREAD_OHOS_H_ ++#define RUNTIME_VM_OS_THREAD_OHOS_H_ ++ ++#if !defined(RUNTIME_VM_OS_THREAD_H_) ++#error Do not include os_thread_linux.h directly; use os_thread.h instead. ++#endif ++ ++#include ++ ++#include "platform/assert.h" ++#include "platform/globals.h" ++ ++namespace dart { ++ ++typedef pthread_key_t ThreadLocalKey; ++typedef pthread_t ThreadId; ++typedef pthread_t ThreadJoinId; ++ ++static const ThreadLocalKey kUnsetThreadLocalKey = ++ static_cast(-1); ++ ++class ThreadInlineImpl { ++ private: ++ ThreadInlineImpl() {} ++ ~ThreadInlineImpl() {} ++ ++ static uword GetThreadLocal(ThreadLocalKey key) { ++ ASSERT(key != kUnsetThreadLocalKey); ++ return reinterpret_cast(pthread_getspecific(key)); ++ } ++ ++ friend class OSThread; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(ThreadInlineImpl); ++}; ++ ++class MutexData { ++ private: ++ MutexData() {} ++ ~MutexData() {} ++ ++ pthread_mutex_t* mutex() { return &mutex_; } ++ ++ pthread_mutex_t mutex_; ++ ++ friend class Mutex; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(MutexData); ++}; ++ ++class MonitorData { ++ private: ++ MonitorData() {} ++ ~MonitorData() {} ++ ++ pthread_mutex_t* mutex() { return &mutex_; } ++ pthread_cond_t* cond() { return &cond_; } ++ ++ pthread_mutex_t mutex_; ++ pthread_cond_t cond_; ++ ++ friend class Monitor; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(MonitorData); ++}; ++ ++} // namespace dart ++ ++#endif // RUNTIME_VM_OS_THREAD_OHOS_H_ +diff --git a/runtime/vm/signal_handler_ohos.cc b/runtime/vm/signal_handler_ohos.cc +new file mode 100644 +index 00000000000..c64c9984606 +--- /dev/null ++++ b/runtime/vm/signal_handler_ohos.cc +@@ -0,0 +1,136 @@ ++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "vm/globals.h" ++#include "vm/instructions.h" ++#include "vm/signal_handler.h" ++#include "vm/simulator.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++namespace dart { ++ ++uintptr_t SignalHandler::GetProgramCounter(const mcontext_t& mcontext) { ++ uintptr_t pc = 0; ++ ++#if defined(HOST_ARCH_IA32) ++ pc = static_cast(mcontext.gregs[REG_EIP]); ++#elif defined(HOST_ARCH_X64) ++ pc = static_cast(mcontext.gregs[REG_RIP]); ++#elif defined(HOST_ARCH_ARM) ++ pc = static_cast(mcontext.arm_pc); ++#elif defined(HOST_ARCH_ARM64) ++ pc = static_cast(mcontext.pc); ++#elif defined(HOST_ARCH_RISCV32) ++ pc = static_cast(mcontext.__gregs[REG_PC]); ++#elif defined(HOST_ARCH_RISCV64) ++ pc = static_cast(mcontext.__gregs[REG_PC]); ++#else ++#error Unsupported architecture. ++#endif // HOST_ARCH_... ++ return pc; ++} ++ ++uintptr_t SignalHandler::GetFramePointer(const mcontext_t& mcontext) { ++ uintptr_t fp = 0; ++ ++#if defined(HOST_ARCH_IA32) ++ fp = static_cast(mcontext.gregs[REG_EBP]); ++#elif defined(HOST_ARCH_X64) ++ fp = static_cast(mcontext.gregs[REG_RBP]); ++#elif defined(HOST_ARCH_ARM) ++ // B1.3.3 Program Status Registers (PSRs) ++ if ((mcontext.arm_cpsr & (1 << 5)) != 0) { ++ // Thumb mode. ++ fp = static_cast(mcontext.arm_r7); ++ } else { ++ // ARM mode. ++ fp = static_cast(mcontext.arm_fp); ++ } ++#elif defined(HOST_ARCH_ARM64) ++ fp = static_cast(mcontext.regs[29]); ++#elif defined(HOST_ARCH_RISCV32) ++ fp = static_cast(mcontext.__gregs[REG_S0]); ++#elif defined(HOST_ARCH_RISCV64) ++ fp = static_cast(mcontext.__gregs[REG_S0]); ++#else ++#error Unsupported architecture. ++#endif // HOST_ARCH_... ++ ++ return fp; ++} ++ ++uintptr_t SignalHandler::GetCStackPointer(const mcontext_t& mcontext) { ++ uintptr_t sp = 0; ++ ++#if defined(HOST_ARCH_IA32) ++ sp = static_cast(mcontext.gregs[REG_ESP]); ++#elif defined(HOST_ARCH_X64) ++ sp = static_cast(mcontext.gregs[REG_RSP]); ++#elif defined(HOST_ARCH_ARM) ++ sp = static_cast(mcontext.arm_sp); ++#elif defined(HOST_ARCH_ARM64) ++ sp = static_cast(mcontext.sp); ++#elif defined(HOST_ARCH_RISCV32) ++ sp = static_cast(mcontext.__gregs[REG_SP]); ++#elif defined(HOST_ARCH_RISCV64) ++ sp = static_cast(mcontext.__gregs[REG_SP]); ++#else ++#error Unsupported architecture. ++#endif // HOST_ARCH_... ++ return sp; ++} ++ ++uintptr_t SignalHandler::GetDartStackPointer(const mcontext_t& mcontext) { ++#if defined(TARGET_ARCH_ARM64) && !defined(USING_SIMULATOR) ++ return static_cast(mcontext.regs[SPREG]); ++#else ++ return GetCStackPointer(mcontext); ++#endif ++} ++ ++uintptr_t SignalHandler::GetLinkRegister(const mcontext_t& mcontext) { ++ uintptr_t lr = 0; ++ ++#if defined(HOST_ARCH_IA32) ++ lr = 0; ++#elif defined(HOST_ARCH_X64) ++ lr = 0; ++#elif defined(HOST_ARCH_ARM) ++ lr = static_cast(mcontext.arm_lr); ++#elif defined(HOST_ARCH_ARM64) ++ lr = static_cast(mcontext.regs[30]); ++#elif defined(HOST_ARCH_RISCV32) ++ lr = static_cast(mcontext.__gregs[REG_RA]); ++#elif defined(HOST_ARCH_RISCV64) ++ lr = static_cast(mcontext.__gregs[REG_RA]); ++#else ++#error Unsupported architecture. ++#endif // HOST_ARCH_... ++ return lr; ++} ++ ++void SignalHandler::Install(SignalAction action) { ++ struct sigaction act = {}; ++ act.sa_handler = NULL; ++ act.sa_sigaction = action; ++ sigemptyset(&act.sa_mask); ++ act.sa_flags = SA_RESTART | SA_SIGINFO; ++ int r = sigaction(SIGPROF, &act, NULL); ++ ASSERT(r == 0); ++} ++ ++void SignalHandler::Remove() { ++ // Ignore future SIGPROF signals because by default SIGPROF will terminate ++ // the process and we may have some signals in flight. ++ struct sigaction act = {}; ++ act.sa_handler = SIG_IGN; ++ sigemptyset(&act.sa_mask); ++ act.sa_flags = 0; ++ int r = sigaction(SIGPROF, &act, NULL); ++ ASSERT(r == 0); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/thread_interrupter_ohos.cc b/runtime/vm/thread_interrupter_ohos.cc +new file mode 100644 +index 00000000000..94e72d58209 +--- /dev/null ++++ b/runtime/vm/thread_interrupter_ohos.cc +@@ -0,0 +1,72 @@ ++// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include // NOLINT ++ ++#include "vm/flags.h" ++#include "vm/os.h" ++#include "vm/profiler.h" ++#include "vm/signal_handler.h" ++#include "vm/thread_interrupter.h" ++ ++namespace dart { ++ ++#ifndef PRODUCT ++ ++DECLARE_FLAG(bool, trace_thread_interrupter); ++ ++class ThreadInterrupterOhos : public AllStatic { ++ public: ++ static void ThreadInterruptSignalHandler(int signal, ++ siginfo_t* info, ++ void* context_) { ++ if (signal != SIGPROF) { ++ return; ++ } ++ Thread* thread = Thread::Current(); ++ if (thread == NULL) { ++ return; ++ } ++ ThreadInterrupter::SampleBufferWriterScope scope; ++ if (!scope.CanSample()) { ++ return; ++ } ++ // Extract thread state. ++ ucontext_t* context = reinterpret_cast(context_); ++ mcontext_t mcontext = context->uc_mcontext; ++ InterruptedThreadState its; ++ its.pc = SignalHandler::GetProgramCounter(mcontext); ++ its.fp = SignalHandler::GetFramePointer(mcontext); ++ its.csp = SignalHandler::GetCStackPointer(mcontext); ++ its.dsp = SignalHandler::GetDartStackPointer(mcontext); ++ its.lr = SignalHandler::GetLinkRegister(mcontext); ++ Profiler::SampleThread(thread, its); ++ } ++}; ++ ++void ThreadInterrupter::InterruptThread(OSThread* thread) { ++ if (FLAG_trace_thread_interrupter) { ++ OS::PrintErr("ThreadInterrupter interrupting %p\n", ++ reinterpret_cast(thread->id())); ++ } ++ int result = pthread_kill(thread->id(), SIGPROF); ++ ASSERT((result == 0) || (result == ESRCH)); ++} ++ ++void ThreadInterrupter::InstallSignalHandler() { ++ SignalHandler::Install(&ThreadInterrupterOhos::ThreadInterruptSignalHandler); ++} ++ ++void ThreadInterrupter::RemoveSignalHandler() { ++ SignalHandler::Remove(); ++} ++ ++#endif // !PRODUCT ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/timeline_ohos.cc b/runtime/vm/timeline_ohos.cc +new file mode 100644 +index 00000000000..27f2e225e34 +--- /dev/null ++++ b/runtime/vm/timeline_ohos.cc +@@ -0,0 +1,120 @@ ++// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file ++// for details. All rights reserved. Use of this source code is governed by a ++// BSD-style license that can be found in the LICENSE file. ++ ++#include "vm/globals.h" ++#if defined(DART_HOST_OS_OHOS) && defined(SUPPORT_TIMELINE) ++ ++#include ++#include ++#include ++ ++#include "platform/atomic.h" ++#include "platform/signal_blocker.h" ++#include "vm/isolate.h" ++#include "vm/json_stream.h" ++#include "vm/lockers.h" ++#include "vm/log.h" ++#include "vm/object.h" ++#include "vm/service_event.h" ++#include "vm/thread.h" ++#include "vm/timeline.h" ++ ++namespace dart { ++ ++DECLARE_FLAG(bool, trace_timeline); ++ ++static int OpenTraceFD() { ++ const char* kSystraceDebugPath = "/sys/kernel/debug/tracing/trace_marker"; ++ const char* kSystracePath = "/sys/kernel/tracing/trace_marker"; ++ ++ int fd = TEMP_FAILURE_RETRY(::open(kSystracePath, O_WRONLY)); ++ if (fd < 0) { ++ fd = TEMP_FAILURE_RETRY(::open(kSystraceDebugPath, O_WRONLY)); ++ } ++ ++ if (fd < 0 && FLAG_trace_timeline) { ++ OS::PrintErr("TimelineEventSystraceRecorder: Could not open `%s` or `%s`\n", ++ kSystraceDebugPath, kSystracePath); ++ } ++ return fd; ++} ++ ++TimelineEventSystraceRecorder::TimelineEventSystraceRecorder() ++ : TimelineEventPlatformRecorder(), systrace_fd_(OpenTraceFD()) { ++ Timeline::set_recorder_discards_clock_values(true); ++} ++ ++TimelineEventSystraceRecorder::~TimelineEventSystraceRecorder() { ++ if (systrace_fd_ >= 0) { ++ close(systrace_fd_); ++ } ++} ++ ++intptr_t TimelineEventSystraceRecorder::PrintSystrace(TimelineEvent* event, ++ char* buffer, ++ intptr_t buffer_size) { ++ ASSERT(buffer != NULL); ++ ASSERT(buffer_size > 0); ++ buffer[0] = '\0'; ++ intptr_t length = 0; ++ int64_t pid = OS::ProcessId(); ++ switch (event->event_type()) { ++ case TimelineEvent::kBegin: { ++ length = Utils::SNPrint(buffer, buffer_size, "B|%" Pd64 "|%s", pid, ++ event->label()); ++ break; ++ } ++ case TimelineEvent::kEnd: { ++ length = Utils::SNPrint(buffer, buffer_size, "E"); ++ break; ++ } ++ case TimelineEvent::kCounter: { ++ if (event->arguments_length() > 0) { ++ // We only report the first counter value. ++ length = Utils::SNPrint(buffer, buffer_size, "C|%" Pd64 "|%s|%s", pid, ++ event->label(), event->arguments()[0].value); ++ } ++ break; ++ } ++ case TimelineEvent::kAsyncBegin: { ++ length = Utils::SNPrint(buffer, buffer_size, "S|%" Pd64 "|%s|%" Pd64 "", ++ pid, event->label(), event->Id()); ++ break; ++ } ++ case TimelineEvent::kAsyncEnd: { ++ length = Utils::SNPrint(buffer, buffer_size, "F|%" Pd64 "|%s|%" Pd64 "", ++ pid, event->label(), event->Id()); ++ break; ++ } ++ default: ++ // Ignore event types that we cannot serialize to the Systrace format. ++ break; ++ } ++ return length; ++} ++ ++void TimelineEventSystraceRecorder::OnEvent(TimelineEvent* event) { ++ if (event == NULL) { ++ return; ++ } ++ if (systrace_fd_ < 0) { ++ return; ++ } ++ ++ // Serialize to the systrace format. ++ const intptr_t kBufferLength = 1024; ++ char buffer[kBufferLength]; ++ const intptr_t event_length = PrintSystrace(event, &buffer[0], kBufferLength); ++ if (event_length > 0) { ++ ssize_t result; ++ // Repeatedly attempt the write while we are being interrupted. ++ do { ++ result = write(systrace_fd_, buffer, event_length); ++ } while ((result == -1L) && (errno == EINTR)); ++ } ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) && !defined(PRODUCT) +diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni +index 4413fdeecb2..41168412aec 100644 +--- a/runtime/vm/vm_sources.gni ++++ b/runtime/vm/vm_sources.gni +@@ -69,6 +69,7 @@ vm_sources = [ + "cpuinfo_android.cc", + "cpuinfo_fuchsia.cc", + "cpuinfo_linux.cc", ++ "cpuinfo_ohos.cc", + "cpuinfo_macos.cc", + "cpuinfo_win.cc", + "dart.cc", +@@ -190,6 +191,7 @@ vm_sources = [ + "native_symbol_android.cc", + "native_symbol_fuchsia.cc", + "native_symbol_linux.cc", ++ "native_symbol_ohos.cc", + "native_symbol_macos.cc", + "native_symbol_win.cc", + "object.cc", +@@ -209,6 +211,7 @@ vm_sources = [ + "os_android.cc", + "os_fuchsia.cc", + "os_linux.cc", ++ "os_ohos.cc", + "os_macos.cc", + "os_thread.cc", + "os_thread.h", +@@ -224,6 +227,8 @@ vm_sources = [ + "os_thread_macos.h", + "os_thread_win.cc", + "os_thread_win.h", ++ "os_thread_ohos.cc", ++ "os_thread_ohos.h", + "os_win.cc", + "parser.cc", + "parser.h", +@@ -292,6 +297,7 @@ vm_sources = [ + "signal_handler_android.cc", + "signal_handler_fuchsia.cc", + "signal_handler_linux.cc", ++ "signal_handler_ohos.cc", + "signal_handler_macos.cc", + "signal_handler_win.cc", + "simulator.h", +@@ -331,6 +337,7 @@ vm_sources = [ + "thread_interrupter_android.cc", + "thread_interrupter_fuchsia.cc", + "thread_interrupter_linux.cc", ++ "thread_interrupter_ohos.cc", + "thread_interrupter_macos.cc", + "thread_interrupter_win.cc", + "thread_pool.cc", +@@ -346,6 +353,7 @@ vm_sources = [ + "timeline_android.cc", + "timeline_fuchsia.cc", + "timeline_linux.cc", ++ "timeline_ohos.cc", + "timeline_macos.cc", + "timer.cc", + "timer.h", +diff --git a/sdk/lib/_internal/vm/bin/vmservice_server.dart b/sdk/lib/_internal/vm/bin/vmservice_server.dart +index b7e5be3eab5..8a8f873f6c5 100644 +--- a/sdk/lib/_internal/vm/bin/vmservice_server.dart ++++ b/sdk/lib/_internal/vm/bin/vmservice_server.dart +@@ -177,11 +177,11 @@ class Server { + this._service, + this._ip, + this._port, +- this._originCheckDisabled, ++ _originCheckDisabled, + bool authCodesDisabled, + this._serviceInfoFilename, + this._enableServicePortFallback) +- : _authCodesDisabled = (authCodesDisabled || Platform.isFuchsia); ++ : _authCodesDisabled = (authCodesDisabled || Platform.isFuchsia),this._originCheckDisabled=true; + + bool _isAllowedOrigin(String origin) { + Uri uri; +diff --git a/sdk_args.gni b/sdk_args.gni +index 4b2a1e4ea63..a864344ee8d 100644 +--- a/sdk_args.gni ++++ b/sdk_args.gni +@@ -28,7 +28,8 @@ declare_args() { + + # Whether to enable the SDK hash check that will prevent loading a kernel + # into a VM which was built with a different SDK. +- verify_sdk_hash = true ++ #verify_sdk_hash = true ++ verify_sdk_hash = false + + # The location in the build output directory of the built Dart SDK. + dart_sdk_output = "dart-sdk" diff --git a/attachment/repos/dart.patch1 b/attachment/repos/dart.patch1 new file mode 100644 index 0000000000000000000000000000000000000000..ebab5d27e1353fa757c6b2deeb80110dada49b04 --- /dev/null +++ b/attachment/repos/dart.patch1 @@ -0,0 +1,972 @@ +diff --git a/BUILD.gn b/BUILD.gn +index a25f379b0ff..c413327b217 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -70,7 +70,7 @@ group("runtime") { + } + } + +- if (is_linux || is_android) { ++ if (is_linux || is_android || is_ohos) { + deps += [ "runtime/bin:abstract_socket_test" ] + } + } +@@ -86,7 +86,7 @@ group("runtime_precompiled") { + "runtime/bin:dart_precompiled_runtime", + "runtime/bin:process_test", + ] +- if (is_linux || is_android) { ++ if (is_linux || is_android || is_ohos) { + deps += [ "runtime/bin:abstract_socket_test" ] + } + } +diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn +index 36c7e9f4fb4..00a1cb64be8 100644 +--- a/build/config/compiler/BUILD.gn ++++ b/build/config/compiler/BUILD.gn +@@ -238,7 +238,7 @@ config("compiler") { + + # Linux/Android common flags setup. + # --------------------------------- +- if (is_linux || is_android) { ++ if (is_linux || is_android || is_ohos) { + ldflags += [ + "-Wl,-z,noexecstack", + "-Wl,-z,now", +@@ -253,14 +253,14 @@ config("compiler") { + # 3. When using the sanitizers. + # Otherwise there is a performance hit, in particular on ia32. + if (is_android || is_asan || is_lsan || is_msan || is_tsan || is_ubsan || +- (is_linux && current_cpu != "x86") || is_fuchsia) { ++ ((is_linux || is_ohos) && current_cpu != "x86") || is_fuchsia) { + cflags += [ "-fPIC" ] + ldflags += [ "-fPIC" ] + } + + # Linux-specific compiler flags setup. + # ------------------------------------ +- if (is_linux) { ++ if (is_linux || is_ohos) { + if (is_clang) { + if (current_cpu == "arm") { + cflags += [ "--target=armv7-linux-gnueabihf" ] +@@ -368,7 +368,7 @@ config("compiler") { + # changes since artifacts from an older version of the toolchain may or may + # not be compatible with newer ones. To achieve this, we insert a synthetic + # define into the compile line. +- if (is_clang && (is_linux || is_mac)) { ++ if (is_clang && (is_linux || is_mac || is_ohos))) { + if (is_linux && host_cpu == "arm64") { + toolchain_stamp_file = + "//buildtools/linux-arm64/clang/.versions/clang.cipd_version" +@@ -465,6 +465,11 @@ config("runtime_library") { + "dl", + "pthread", + ] ++ } else if (is_ohos) { ++ libs += [ ++ "dl", ++ "pthread", ++ ] + } else if (is_android) { + # Android standard library setup. + if (is_clang) { +@@ -607,7 +612,7 @@ if (is_win) { + + # The Raspberry Pi 1 toolchain enables this warning, but Dart doesn't build + # cleanly with it. +- if (is_linux && !is_clang && current_cpu == "arm" && arm_version == 6) { ++ if ((is_linux || is_ohos) && !is_clang && current_cpu == "arm" && arm_version == 6) { + default_warning_flags += [ "-Wno-type-limits" ] + } + + +\ No newline at end of file +diff --git a/build/config/sanitizers/sanitizers.gni b/build/config/sanitizers/sanitizers.gni +index c7da74b09fe..8c11b3401dc 100644 +--- a/build/config/sanitizers/sanitizers.gni ++++ b/build/config/sanitizers/sanitizers.gni +@@ -7,7 +7,7 @@ declare_args() { + # buildtools/third_party/libc++abi) instead of stdlibc++ as standard library. + # This is intended to be used for instrumented builds. + use_custom_libcxx = +- (is_asan && is_linux) || is_lsan || is_msan || is_tsan || is_ubsan ++ (is_asan && (is_linux || is_ohos)) || is_lsan || is_msan || is_tsan || is_ubsan + + # Track where uninitialized memory originates from. From fastest to slowest: + # 0 - no tracking, 1 - track only the initial allocation site, 2 - track the +diff --git a/build/config/sysroot.gni b/build/config/sysroot.gni +index fce65c42d21..d506de46045 100644 +--- a/build/config/sysroot.gni ++++ b/build/config/sysroot.gni +@@ -14,7 +14,7 @@ declare_args() { + dart_use_debian_sysroot = false + } + +-if (is_linux && dart_use_debian_sysroot) { ++if ((is_linux || is_ohos) && dart_use_debian_sysroot) { + if (current_cpu == "x86") { + target_sysroot = + rebase_path("//build/linux/debian_stretch_i386-sysroot", root_build_dir) +diff --git a/build/rust/rust.gni b/build/rust/rust.gni +index 0a8cdc13970..8bdbf6a1ff5 100644 +--- a/build/rust/rust.gni ++++ b/build/rust/rust.gni +@@ -27,6 +27,8 @@ template("rust_library") { + cargo_out_dir = target_out_dir + if (is_linux) { + rust_os = "unknown-linux-gnu" ++ } else if (is_ohos) { ++ rust_os = "unknown-linux-gnu" + } else if (is_mac) { + rust_os = "apple-darwin" + } else if (is_win) { +diff --git a/build/sanitizers/BUILD.gn b/build/sanitizers/BUILD.gn +index e9f8c7a55cd..b1df3aef772 100644 +--- a/build/sanitizers/BUILD.gn ++++ b/build/sanitizers/BUILD.gn +@@ -2,7 +2,7 @@ + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +-if (is_linux && !is_chromeos) { ++if ((is_linux || is_ohos) && !is_chromeos) { + # TODO(GYP): Figure out which of these work and are needed on other platforms. + copy("copy_llvm_symbolizer") { + if (is_win) { +diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn +index 042b6a45f7e..7f89f9fcd06 100644 +--- a/runtime/BUILD.gn ++++ b/runtime/BUILD.gn +@@ -87,8 +87,10 @@ config("dart_precompiler_config") { + + config("dart_os_config") { + defines = [] +- if (target_os == "linux") { ++ if (target_os == "linux" && !is_mac) { + target_os = "ohos" ++ } else if (is_mac){ ++ target_os = "mac" + } + print("DART TARGET OS: "+target_os ) + if (target_os == "android") { +diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn +index 5eecfb63c08..14548125acb 100644 +--- a/runtime/bin/BUILD.gn ++++ b/runtime/bin/BUILD.gn +@@ -33,8 +33,13 @@ config("libdart_builtin_config") { + ] + } + +- print("dart runtime is_win:%b" ,is_win) +- print("dart runtime is_ohos:%b" , is_ohos) ++ if (is_linux && is_ohos) { ++ is_linux = false ++ } ++ ++ print("dart runtime is_linux:" ,is_linux) ++ print("dart runtime is_win:" ,is_win) ++ print("dart runtime is_ohos:" , is_ohos) + if(is_ohos && !is_win){ + libs +=[ + "hilog_ndk.z" +@@ -1063,7 +1068,7 @@ shared_library("ffi_test_dynamic_library") { + # The only effect of DART_SHARED_LIB is to export the Dart API. + "DART_SHARED_LIB", + ] +- if (is_linux || is_android) { ++ if (is_linux || is_android || is_ohos) { + cflags = [ "-fPIC" ] + } + if (is_win) { +diff --git a/runtime/bin/abstract_socket_test.cc b/runtime/bin/abstract_socket_test.cc +index fab2f26fe49..3aacb9058b4 100644 +--- a/runtime/bin/abstract_socket_test.cc ++++ b/runtime/bin/abstract_socket_test.cc +@@ -8,7 +8,7 @@ + // closes the connection and UNIX socket. + + #include "platform/globals.h" +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + + #include + #include +@@ -101,4 +101,4 @@ int main() { + return -1; + } + +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/address_sanitizer.cc b/runtime/bin/address_sanitizer.cc +index 4b8776df7a5..5f3b9ce4f2f 100644 +--- a/runtime/bin/address_sanitizer.cc ++++ b/runtime/bin/address_sanitizer.cc +@@ -4,7 +4,7 @@ + + #include "platform/globals.h" + +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOSX) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOSX) || defined(DART_HOST_OS_OHOS) + #if defined(__has_feature) + #if __has_feature(address_sanitizer) + #if !defined(GOOGLE3) +@@ -25,4 +25,4 @@ __asan_default_options() { + #endif // !defined(GOOGLE3) + #endif // __has_feature(address_sanitizer) + #endif // defined(__has_feature) +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOSX) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOSX) || defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/builtin_impl_sources.gni b/runtime/bin/builtin_impl_sources.gni +index d93fa28e4ee..1a37194d0be 100644 +--- a/runtime/bin/builtin_impl_sources.gni ++++ b/runtime/bin/builtin_impl_sources.gni +@@ -14,6 +14,7 @@ builtin_impl_sources = [ + "crypto_linux.cc", + "crypto_macos.cc", + "crypto_win.cc", ++ "crypto_ohos.cc", + "dartutils.cc", + "dartutils.h", + "directory.cc", +@@ -31,11 +32,13 @@ builtin_impl_sources = [ + "fdutils_fuchsia.cc", + "fdutils_linux.cc", + "fdutils_macos.cc", ++ "fdutils_ohos.cc", + "file.cc", + "file.h", + "file_android.cc", + "file_fuchsia.cc", + "file_linux.cc", ++ "file_ohos.cc", + "file_macos.cc", + "file_support.cc", + "file_win.cc", +@@ -58,10 +61,13 @@ builtin_impl_sources = [ + "thread_macos.h", + "thread_win.cc", + "thread_win.h", ++ "thread_ohos.cc", ++ "thread_ohos.h", + "utils.cc", + "utils.h", + "utils_android.cc", + "utils_fuchsia.cc", ++ "utils_ohos.cc", + "utils_linux.cc", + "utils_macos.cc", + "utils_win.cc", +diff --git a/runtime/bin/console_posix.cc b/runtime/bin/console_posix.cc +index f318ae1ef6a..59c2b6c6509 100644 +--- a/runtime/bin/console_posix.cc ++++ b/runtime/bin/console_posix.cc +@@ -4,7 +4,7 @@ + + #include "platform/globals.h" + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + + #include "bin/console.h" + +@@ -28,4 +28,4 @@ void Console::RestoreConfig() { + } // namespace dart + + #endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/eventhandler.h b/runtime/bin/eventhandler.h +index e2635e0cb20..dbb8051d162 100644 +--- a/runtime/bin/eventhandler.h ++++ b/runtime/bin/eventhandler.h +@@ -595,6 +595,8 @@ class DescriptorInfoMultipleMixin : public DI { + #include "bin/eventhandler_macos.h" + #elif defined(DART_HOST_OS_WINDOWS) + #include "bin/eventhandler_win.h" ++#elif defined(DART_HOST_OS_OHOS) ++#include "bin/eventhandler_ohos.h" + #else + #error Unknown target os. + #endif +diff --git a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc +index d87ae66e9fd..5ec7ff6a97b 100644 +--- a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc ++++ b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc +@@ -1287,7 +1287,7 @@ DART_EXPORT void SetFfiNativeResolverForTest(Dart_Handle url) { + //////////////////////////////////////////////////////////////////////////////// + + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || \ +- defined(DART_HOST_OS_MACOS) ++ defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + static bool Regress216834909_hang_at_exit = true; + + static void Regress216834909_AtExit() { +@@ -1309,6 +1309,6 @@ DART_EXPORT void Regress216834909_SetAtExit(int64_t install) { + } + } + #endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || +- // defined(DART_HOST_OS_MACOS) ++ // defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + + } // namespace dart +diff --git a/runtime/bin/main_options.cc b/runtime/bin/main_options.cc +index ff9c3d1672d..31940a88072 100644 +--- a/runtime/bin/main_options.cc ++++ b/runtime/bin/main_options.cc +@@ -239,7 +239,7 @@ void Options::PrintUsage() { + " use for secure socket connections.\n" + #if defined(DART_HOST_OS_LINUX) || \ + defined(DART_HOST_OS_ANDROID) || \ +- defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + "--namespace=\n" + " The path to a directory that dart:io calls will treat as the root of the\n" + " filesystem.\n" +diff --git a/runtime/bin/process.h b/runtime/bin/process.h +index 5a6adfdc6fc..0dfff8a0e26 100644 +--- a/runtime/bin/process.h ++++ b/runtime/bin/process.h +@@ -349,7 +349,7 @@ class BufferListBase { + }; + + #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || \ +- defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) ++ defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + class BufferList : public BufferListBase { + public: + BufferList() {} +diff --git a/runtime/bin/socket.cc b/runtime/bin/socket.cc +index 708bda49461..8bd55b4e8f6 100644 +--- a/runtime/bin/socket.cc ++++ b/runtime/bin/socket.cc +@@ -209,12 +209,12 @@ Dart_Handle ListeningSocketRegistry::CreateUnixDomainBindListen( + return result; + } + +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + // Abstract unix domain socket doesn't exist in file system. + if (File::Exists(namespc, addr.un.sun_path) && path[0] != '@') { + #else + if (File::Exists(namespc, addr.un.sun_path)) { +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + if (unix_domain_sockets_ != nullptr) { + // If there is a socket listening on this file. Ensure + // that it was created with `shared` mode and current `shared` +@@ -286,7 +286,7 @@ bool ListeningSocketRegistry::CloseOneSafe(OSSocket* os_socket, + } + // Unlink the socket file, if os_socket contains unix domain sockets. + if (os_socket->address.addr.sa_family == AF_UNIX) { +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + // If the socket is abstract, which has a path starting with a null byte, + // unlink() is not necessary because the file doesn't exist. + if (os_socket->address.un.sun_path[0] != '\0') { +@@ -294,7 +294,7 @@ bool ListeningSocketRegistry::CloseOneSafe(OSSocket* os_socket, + } + #else + Utils::Unlink(os_socket->address.un.sun_path); +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + // Remove os_socket from unix_domain_sockets_ list. + OSSocket* prev = nullptr; + OSSocket* current = unix_domain_sockets_; +diff --git a/runtime/bin/socket.h b/runtime/bin/socket.h +index b0efe349a4a..69413ce2247 100644 +--- a/runtime/bin/socket.h ++++ b/runtime/bin/socket.h +@@ -262,7 +262,7 @@ class ListeningSocketRegistry { + const char* path) { + while (current != NULL) { + ASSERT(current->address.addr.sa_family == AF_UNIX); +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + bool condition; + if (path[0] == '\0') { + condition = current->address.un.sun_path[0] == '\0' && +@@ -280,7 +280,7 @@ class ListeningSocketRegistry { + namespc, path) == File::kIdentical) { + return current; + } +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + current = current->next; + } + return NULL; +diff --git a/runtime/bin/socket_base.cc b/runtime/bin/socket_base.cc +index de4f2598d69..33a7414c656 100644 +--- a/runtime/bin/socket_base.cc ++++ b/runtime/bin/socket_base.cc +@@ -128,7 +128,7 @@ void SocketAddress::GetSockAddr(Dart_Handle obj, RawAddr* addr) { + Dart_Handle SocketAddress::GetUnixDomainSockAddr(const char* path, + Namespace* namespc, + RawAddr* addr) { +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + NamespaceScope ns(namespc, path); + path = ns.path(); + bool is_abstract = (path[0] == '@'); +@@ -139,7 +139,7 @@ Dart_Handle SocketAddress::GetUnixDomainSockAddr(const char* path, + // connection will be rejected. + bzero(addr->un.sun_path, sizeof(addr->un.sun_path)); + } +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + if (sizeof(path) > sizeof(addr->un.sun_path)) { + OSError os_error(-1, + "The length of path exceeds the limit. " +@@ -149,12 +149,12 @@ Dart_Handle SocketAddress::GetUnixDomainSockAddr(const char* path, + } + addr->un.sun_family = AF_UNIX; + Utils::SNPrint(addr->un.sun_path, sizeof(addr->un.sun_path), "%s", path); +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + // In case of abstract namespace, transfer the leading '@' into a null byte. + if (is_abstract) { + addr->un.sun_path[0] = '\0'; + } +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + return Dart_Null(); + } + +diff --git a/runtime/bin/socket_base.h b/runtime/bin/socket_base.h +index 320165323f6..85adadd8ecb 100644 +--- a/runtime/bin/socket_base.h ++++ b/runtime/bin/socket_base.h +@@ -11,6 +11,8 @@ + #include "bin/socket_base_android.h" + #elif defined(DART_HOST_OS_FUCHSIA) + #include "bin/socket_base_fuchsia.h" ++#elif defined(DART_HOST_OS_OHOS) ++#include "bin/socket_base_ohos.h" + #elif defined(DART_HOST_OS_LINUX) + #include "bin/socket_base_linux.h" + #elif defined(DART_HOST_OS_MACOS) +@@ -87,7 +89,7 @@ class SocketAddress { + + private: + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + // Unix domain address is only on Linux, Mac OS and Android now. + // unix(7) require sun_path to be 108 bytes on Linux and Android, 104 bytes on + // Mac OS. +@@ -97,7 +99,7 @@ class SocketAddress { + #else + char as_string_[INET6_ADDRSTRLEN]; + #endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- // defined(DART_HOST_OS_ANDROID) ++ // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + RawAddr addr_; + + DISALLOW_COPY_AND_ASSIGN(SocketAddress); +diff --git a/runtime/bin/socket_base_posix.cc b/runtime/bin/socket_base_posix.cc +index e5c48c22208..c0b542d7d27 100644 +--- a/runtime/bin/socket_base_posix.cc ++++ b/runtime/bin/socket_base_posix.cc +@@ -3,7 +3,7 @@ + // BSD-style license that can be found in the LICENSE file. + + #include "platform/globals.h" +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + #include "bin/socket_base.h" + + #include // NOLINT +@@ -519,4 +519,4 @@ bool SocketBase::SetOption(intptr_t fd, + } // namespace dart + + #endif // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \ +- // defined(DART_HOST_OS_MACOS) ++ // defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/thread.h b/runtime/bin/thread.h +index 18a088f30a7..da06deb7a55 100644 +--- a/runtime/bin/thread.h ++++ b/runtime/bin/thread.h +@@ -24,6 +24,8 @@ class Monitor; + #include "bin/thread_fuchsia.h" + #elif defined(DART_HOST_OS_LINUX) + #include "bin/thread_linux.h" ++#elif defined(DART_HOST_OS_OHOS) ++#include "bin/thread_ohos.h" + #elif defined(DART_HOST_OS_MACOS) + #include "bin/thread_macos.h" + #elif defined(DART_HOST_OS_WINDOWS) +diff --git a/runtime/bin/thread_absl.cc b/runtime/bin/thread_absl.cc +index b0163922918..4dbc37830a3 100644 +--- a/runtime/bin/thread_absl.cc ++++ b/runtime/bin/thread_absl.cc +@@ -71,7 +71,7 @@ static void* ThreadStart(void* data_ptr) { + uword parameter = data->parameter(); + delete data; + +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + // Set the thread name. There is 16 bytes limit on the name (including \0). + // pthread_setname_np ignores names that are too long rather than truncating. + char truncated_name[16]; +diff --git a/runtime/bin/thread_ohos.h b/runtime/bin/thread_ohos.h +index 2d2b5fbb12c..c15799d45f7 100644 +--- a/runtime/bin/thread_ohos.h ++++ b/runtime/bin/thread_ohos.h +@@ -6,7 +6,7 @@ + #define RUNTIME_BIN_THREAD_OHOS_H_ + + #if !defined(RUNTIME_BIN_THREAD_H_) +-#error Do not include thread_linux.h directly; use thread.h instead. ++#error Do not include thread_ohos.h directly; use thread.h instead. + #endif + + #include +diff --git a/runtime/bin/virtual_memory_posix.cc b/runtime/bin/virtual_memory_posix.cc +index 36185648ccd..e061ab47ec9 100644 +--- a/runtime/bin/virtual_memory_posix.cc ++++ b/runtime/bin/virtual_memory_posix.cc +@@ -4,7 +4,7 @@ + + #include "platform/globals.h" + #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \ +- defined(DART_HOST_OS_MACOS) ++ defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + + #include "bin/virtual_memory.h" + +diff --git a/runtime/lib/ffi_dynamic_library.cc b/runtime/lib/ffi_dynamic_library.cc +index e04c881411e..fb5f9dd1993 100644 +--- a/runtime/lib/ffi_dynamic_library.cc ++++ b/runtime/lib/ffi_dynamic_library.cc +@@ -19,7 +19,7 @@ + #include "vm/object_store.h" + + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + #include + #endif + +@@ -168,7 +168,7 @@ DEFINE_NATIVE_ENTRY(Ffi_dl_open, 0, 1) { + + DEFINE_NATIVE_ENTRY(Ffi_dl_processLibrary, 0, 0) { + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + return DynamicLibrary::New(RTLD_DEFAULT); + #else + return DynamicLibrary::New(kWindowsDynamicLibraryProcessPtr); +diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h +index 7122f9f7d08..f8a861e6c54 100644 +--- a/runtime/platform/globals.h ++++ b/runtime/platform/globals.h +@@ -108,6 +108,10 @@ + // Check for Android first, to determine its difference from Linux. + #define DART_HOST_OS_ANDROID 1 + ++#elif defined(DART_TARGET_OS_OHOS) && !defined(_WIN32) ++ ++#define DART_HOST_OS_OHOS 1 ++ + #elif defined(__linux__) || defined(__FreeBSD__) + + // Generic Linux. +@@ -746,6 +750,8 @@ DART_FORCE_INLINE D bit_copy(const S& source) { + #define kHostOperatingSystemName "macos" + #elif defined(DART_HOST_OS_WINDOWS) + #define kHostOperatingSystemName "windows" ++#elif defined(DART_HOST_OS_OHOS) ++#define kHostOperatingSystemName "ohos" + #else + #error Host operating system detection failed. + #endif +@@ -790,10 +796,10 @@ DART_FORCE_INLINE D bit_copy(const S& source) { + #define kTargetOperatingSystemName "android" + #elif defined(DART_TARGET_OS_FUCHSIA) + #define kTargetOperatingSystemName "fuchsia" +-#elif defined(DART_TARGET_OS_LINUX) +-#define kTargetOperatingSystemName "linux" + #elif defined(DART_TARGET_OS_OHOS) + #define kTargetOperatingSystemName "ohos" ++#elif defined(DART_TARGET_OS_LINUX) ++#define kTargetOperatingSystemName "linux" + #elif defined(DART_TARGET_OS_MACOS_IOS) + #define kTargetOperatingSystemName "ios" + #elif defined(DART_TARGET_OS_MACOS) +diff --git a/runtime/platform/utils.cc b/runtime/platform/utils.cc +index f8bd278785f..2ddcf679720 100644 +--- a/runtime/platform/utils.cc ++++ b/runtime/platform/utils.cc +@@ -8,7 +8,7 @@ + #include "platform/globals.h" + + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + #include + #endif + +@@ -314,7 +314,7 @@ static void GetLastErrorAsString(char** error) { + if (error == nullptr) return; // Nothing to do. + + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + const char* status = dlerror(); + *error = status != nullptr ? strdup(status) : nullptr; + #elif defined(DART_HOST_OS_WINDOWS) +@@ -329,7 +329,7 @@ void* Utils::LoadDynamicLibrary(const char* library_path, char** error) { + void* handle = nullptr; + + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + handle = dlopen(library_path, RTLD_LAZY); + #elif defined(DART_HOST_OS_WINDOWS) + SetLastError(0); // Clear any errors. +@@ -364,7 +364,7 @@ void* Utils::ResolveSymbolInDynamicLibrary(void* library_handle, + void* result = nullptr; + + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + dlerror(); // Clear any errors. + result = dlsym(library_handle, symbol); + // Note: nullptr might be a valid return from dlsym. Must call dlerror +@@ -388,7 +388,7 @@ void Utils::UnloadDynamicLibrary(void* library_handle, char** error) { + bool ok = false; + + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_OHOS) + ok = dlclose(library_handle) == 0; + #elif defined(DART_HOST_OS_WINDOWS) + SetLastError(0); // Clear any errors. +diff --git a/runtime/platform/utils.h b/runtime/platform/utils.h +index a4caafda349..8ac51cef68a 100644 +--- a/runtime/platform/utils.h ++++ b/runtime/platform/utils.h +@@ -567,6 +567,8 @@ class Utils { + #include "platform/utils_fuchsia.h" + #elif defined(DART_HOST_OS_LINUX) + #include "platform/utils_linux.h" ++#elif defined(DART_HOST_OS_OHOS) ++#include "platform/utils_ohos.h" + #elif defined(DART_HOST_OS_MACOS) + #include "platform/utils_macos.h" + #elif defined(DART_HOST_OS_WINDOWS) +diff --git a/runtime/platform/utils_ohos.h b/runtime/platform/utils_ohos.h +index b33c3291826..abb486ed2d4 100644 +--- a/runtime/platform/utils_ohos.h ++++ b/runtime/platform/utils_ohos.h +@@ -6,7 +6,7 @@ + #define RUNTIME_PLATFORM_UTILS_OHOS_H_ + + #if !defined(RUNTIME_PLATFORM_UTILS_H_) +-#error Do not include utils_linux.h directly; use utils.h instead. ++#error Do not include utils_ohos.h directly; use utils.h instead. + #endif + + #include // NOLINT +diff --git a/runtime/vm/compiler/ffi/abi.cc b/runtime/vm/compiler/ffi/abi.cc +index cc5eb834b3d..243aca8daf3 100644 +--- a/runtime/vm/compiler/ffi/abi.cc ++++ b/runtime/vm/compiler/ffi/abi.cc +@@ -31,7 +31,7 @@ static_assert(offsetof(AbiAlignmentUint64, i) == 8, + "FFI transformation alignment"); + #elif (defined(HOST_ARCH_IA32) && /* NOLINT(whitespace/parens) */ \ + (defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID))) || \ ++ defined(DART_HOST_OS_ANDROID)) || defined(DART_HOST_OS_OHOS)) || \ + (defined(HOST_ARCH_ARM) && defined(DART_HOST_OS_IOS)) + static_assert(offsetof(AbiAlignmentDouble, d) == 4, + "FFI transformation alignment"); +diff --git a/runtime/vm/cpu_arm.cc b/runtime/vm/cpu_arm.cc +index 04c3aefb682..f3975f10c34 100644 +--- a/runtime/vm/cpu_arm.cc ++++ b/runtime/vm/cpu_arm.cc +@@ -85,7 +85,7 @@ void CPU::FlushICache(uword start, uword size) { + // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sys_icache_invalidate.3.html + #if defined(DART_HOST_OS_IOS) + sys_icache_invalidate(reinterpret_cast(start), size); +-#elif defined(DART_HOST_OS_LINUX) ++#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + char* beg = reinterpret_cast(start); + char* end = reinterpret_cast(start + size); + __builtin___clear_cache(beg, end); +diff --git a/runtime/vm/cpu_arm64.cc b/runtime/vm/cpu_arm64.cc +index 10827ba7c45..cbf5358d7c2 100644 +--- a/runtime/vm/cpu_arm64.cc ++++ b/runtime/vm/cpu_arm64.cc +@@ -41,7 +41,7 @@ void CPU::FlushICache(uword start, uword size) { + // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sys_icache_invalidate.3.html + #if defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_IOS) + sys_icache_invalidate(reinterpret_cast(start), size); +-#elif defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#elif defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + char* beg = reinterpret_cast(start); + char* end = reinterpret_cast(start + size); + __builtin___clear_cache(beg, end); +diff --git a/runtime/vm/cpu_riscv.cc b/runtime/vm/cpu_riscv.cc +index e7ab8fe083b..20eceb4b308 100644 +--- a/runtime/vm/cpu_riscv.cc ++++ b/runtime/vm/cpu_riscv.cc +@@ -37,7 +37,7 @@ void CPU::FlushICache(uword start, uword size) { + + #if defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_IOS) + sys_icache_invalidate(reinterpret_cast(start), size); +-#elif defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#elif defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + char* beg = reinterpret_cast(start); + char* end = reinterpret_cast(start + size); + __builtin___clear_cache(beg, end); +diff --git a/runtime/vm/malloc_hooks_unsupported.cc b/runtime/vm/malloc_hooks_unsupported.cc +index 11bc11521e5..2954eb67ac3 100644 +--- a/runtime/vm/malloc_hooks_unsupported.cc ++++ b/runtime/vm/malloc_hooks_unsupported.cc +@@ -10,7 +10,7 @@ + + #include "vm/json_stream.h" + +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + #include + #elif defined(DART_HOST_OS_MACOS) + #include +diff --git a/runtime/vm/os_thread.h b/runtime/vm/os_thread.h +index e125847dc07..8f525e29444 100644 +--- a/runtime/vm/os_thread.h ++++ b/runtime/vm/os_thread.h +@@ -21,6 +21,8 @@ + #include "vm/os_thread_fuchsia.h" + #elif defined(DART_HOST_OS_LINUX) + #include "vm/os_thread_linux.h" ++#elif defined(DART_HOST_OS_OHOS) ++#include "vm/os_thread_ohos.h" + #elif defined(DART_HOST_OS_MACOS) + #include "vm/os_thread_macos.h" + #elif defined(DART_HOST_OS_WINDOWS) +diff --git a/runtime/vm/os_thread_absl.cc b/runtime/vm/os_thread_absl.cc +index e5887c3fae4..93feb2749a1 100644 +--- a/runtime/vm/os_thread_absl.cc ++++ b/runtime/vm/os_thread_absl.cc +@@ -107,7 +107,7 @@ static void UnblockSIGPROF() { + // is used to ensure that the thread is properly destroyed if the thread just + // exits. + static void* ThreadStart(void* data_ptr) { +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + if (FLAG_worker_thread_priority != kMinInt) { + if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), + FLAG_worker_thread_priority) == -1) { +@@ -138,7 +138,7 @@ static void* ThreadStart(void* data_ptr) { + uword parameter = data->parameter(); + delete data; + +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + // Set the thread name. There is 16 bytes limit on the name (including \0). + // pthread_setname_np ignores names that are too long rather than truncating. + char truncated_name[16]; +@@ -221,7 +221,7 @@ ThreadId OSThread::GetCurrentThreadId() { + ThreadId OSThread::GetCurrentThreadTraceId() { + #if defined(DART_HOST_OS_ANDROID) + return GetCurrentThreadId(); +-#elif defined(DART_HOST_OS_LINUX) ++#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + return syscall(__NR_gettid); + #elif defined(DART_HOST_OS_MACOS) + return ThreadIdFromIntPtr(pthread_mach_thread_np(pthread_self())); +@@ -249,7 +249,7 @@ void OSThread::Join(ThreadJoinId id) { + + intptr_t OSThread::ThreadIdToIntPtr(ThreadId id) { + ASSERT(sizeof(id) == sizeof(intptr_t)); +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + return static_cast(id); + #elif defined(DART_HOST_OS_MACOS) + return reinterpret_cast(id); +@@ -257,7 +257,7 @@ intptr_t OSThread::ThreadIdToIntPtr(ThreadId id) { + } + + ThreadId OSThread::ThreadIdFromIntPtr(intptr_t id) { +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + return static_cast(id); + #elif defined(DART_HOST_OS_MACOS) + return reinterpret_cast(id); +@@ -269,7 +269,7 @@ bool OSThread::Compare(ThreadId a, ThreadId b) { + } + + bool OSThread::GetCurrentStackBounds(uword* lower, uword* upper) { +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + pthread_attr_t attr; + // May fail on the main thread. + if (pthread_getattr_np(pthread_self(), &attr) != 0) { +diff --git a/runtime/vm/proccpuinfo.cc b/runtime/vm/proccpuinfo.cc +index 066e3f7a5f6..0a8ce77b2ca 100644 +--- a/runtime/vm/proccpuinfo.cc ++++ b/runtime/vm/proccpuinfo.cc +@@ -3,7 +3,7 @@ + // BSD-style license that can be found in the LICENSE file. + + #include "vm/globals.h" +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + + #include "vm/proccpuinfo.h" + +@@ -146,4 +146,4 @@ bool ProcCpuInfo::HasField(const char* field) { + + } // namespace dart + +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/proccpuinfo.h b/runtime/vm/proccpuinfo.h +index ff249d42d2d..4c829f4ace6 100644 +--- a/runtime/vm/proccpuinfo.h ++++ b/runtime/vm/proccpuinfo.h +@@ -6,7 +6,7 @@ + #define RUNTIME_VM_PROCCPUINFO_H_ + + #include "vm/globals.h" +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + + #include "vm/allocation.h" + +@@ -29,6 +29,6 @@ class ProcCpuInfo : public AllStatic { + + } // namespace dart + +-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + + #endif // RUNTIME_VM_PROCCPUINFO_H_ +diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc +index 4ad80c5e5a9..080cebc5e72 100644 +--- a/runtime/vm/profiler.cc ++++ b/runtime/vm/profiler.cc +@@ -399,7 +399,7 @@ void Profiler::DumpStackTrace(void* context) { + return; + } + #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ +- defined(DART_HOST_OS_ANDROID) ++ defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + ucontext_t* ucontext = reinterpret_cast(context); + mcontext_t mcontext = ucontext->uc_mcontext; + uword pc = SignalHandler::GetProgramCounter(mcontext); +diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc +index ec4385f869d..3f65586df68 100644 +--- a/runtime/vm/service.cc ++++ b/runtime/vm/service.cc +@@ -4587,7 +4587,7 @@ static void RequestHeapSnapshot(Thread* thread, JSONStream* js) { + PrintSuccess(js); + } + +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + struct VMMapping { + char path[256]; + size_t size; +@@ -4786,7 +4786,7 @@ static intptr_t GetProcessMemoryUsageHelper(JSONStream* js) { + } + #endif + +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + AddVMMappings(&rss_children); + #endif + // TODO(46166): Implement for other operating systems. +diff --git a/runtime/vm/signal_handler.h b/runtime/vm/signal_handler.h +index cabe1498f93..36fccd7a358 100644 +--- a/runtime/vm/signal_handler.h ++++ b/runtime/vm/signal_handler.h +@@ -33,6 +33,9 @@ typedef struct ucontext { + struct siginfo_t; + struct mcontext_t; + struct sigset_t {}; ++#elif defined(DART_HOST_OS_OHOS) ++#include // NOLINT ++#include // NOLINT + #elif defined(DART_HOST_OS_FUCHSIA) + #include // NOLINT + #include // NOLINT +diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc +index 3d2177af165..7eddaf7ed24 100644 +--- a/runtime/vm/timeline.cc ++++ b/runtime/vm/timeline.cc +@@ -131,7 +131,7 @@ static TimelineEventRecorder* CreateTimelineRecorder() { + + // Systrace recorder. + if (strcmp("systrace", flag) == 0) { +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + return new TimelineEventSystraceRecorder(); + #elif defined(DART_HOST_OS_MACOS) + if (__builtin_available(iOS 12.0, macOS 10.14, *)) { +diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h +index 5c409a836bc..b84e3df2f20 100644 +--- a/runtime/vm/timeline.h ++++ b/runtime/vm/timeline.h +@@ -1046,7 +1046,7 @@ class TimelineEventFuchsiaRecorder : public TimelineEventPlatformRecorder { + }; + #endif // defined(DART_HOST_OS_FUCHSIA) + +-#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + // A recorder that writes events to Android Systrace. This class is exposed in + // this header file only so that PrintSystrace can be visible to + // timeline_test.cc. +@@ -1067,7 +1067,7 @@ class TimelineEventSystraceRecorder : public TimelineEventPlatformRecorder { + + int systrace_fd_; + }; +-#endif // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ++#endif // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_OHOS) + + #if defined(DART_HOST_OS_MACOS) + // A recorder that sends events to Macos's tracing app. See: +diff --git a/runtime/vm/virtual_memory_posix.cc b/runtime/vm/virtual_memory_posix.cc +index 21f6f7900cf..93c83c17852 100644 +--- a/runtime/vm/virtual_memory_posix.cc ++++ b/runtime/vm/virtual_memory_posix.cc +@@ -4,7 +4,7 @@ + + #include "vm/globals.h" + #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \ +- defined(DART_HOST_OS_MACOS) ++ defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + + #include "vm/virtual_memory.h" + +@@ -199,7 +199,7 @@ void VirtualMemory::Init() { + } + #endif // defined(DUAL_MAPPING_SUPPORTED) + +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ++#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_OHOS) + FILE* fp = fopen("/proc/sys/vm/max_map_count", "r"); + if (fp != nullptr) { + size_t max_map_count = 0; +@@ -577,4 +577,4 @@ void VirtualMemory::DontNeed(void* address, intptr_t size) { + } // namespace dart + + #endif // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \ +- // defined(DART_HOST_OS_MACOS) ++ // defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) +diff --git a/sdk/lib/io/platform.dart b/sdk/lib/io/platform.dart +index b302b4a0396..5d8b9cbc34f 100644 +--- a/sdk/lib/io/platform.dart ++++ b/sdk/lib/io/platform.dart +@@ -156,6 +156,9 @@ class Platform { + /// [Fuchsia](https://en.wikipedia.org/wiki/Google_Fuchsia). + static final bool isFuchsia = (_operatingSystem == "fuchsia"); + ++ /// Whether the operating system is a version of Ohos ++ static final bool isOhos = (_operatingSystem == "ohos"); ++ + /// The environment for this process as a map from string key to string value. + /// + /// The map is unmodifiable, diff --git a/attachment/repos/dart.patch2 b/attachment/repos/dart.patch2 new file mode 100644 index 0000000000000000000000000000000000000000..04bfb0b5d8d315f8809983b58e70df2bd2aafcd1 --- /dev/null +++ b/attachment/repos/dart.patch2 @@ -0,0 +1,240 @@ +diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn +index 7f89f9fcd06..edeb765c7e4 100644 +--- a/runtime/BUILD.gn ++++ b/runtime/BUILD.gn +@@ -87,11 +87,6 @@ config("dart_precompiler_config") { + + config("dart_os_config") { + defines = [] +- if (target_os == "linux" && !is_mac) { +- target_os = "ohos" +- } else if (is_mac){ +- target_os = "mac" +- } + print("DART TARGET OS: "+target_os ) + if (target_os == "android") { + defines += [ "DART_TARGET_OS_ANDROID" ] +@@ -104,7 +99,9 @@ config("dart_os_config") { + defines += [ "DART_TARGET_OS_LINUX" ] + } else if (target_os == "ohos") { + defines += [ "DART_TARGET_OS_OHOS" ] +- print("DART TARGET OS OHOS") ++ if (is_ohos) { ++ defines += [ "DART_RUNTIME_OS_OHOS" ] ++ } + } else if (target_os == "mac") { + defines += [ "DART_TARGET_OS_MACOS" ] + } else if (target_os == "win") { +@@ -255,6 +252,11 @@ config("dart_config") { + cflags += [ "-O3" ] + } + ++ ++ if (is_ohos) { ++ ldflags += ["-lhilog_ndk.z"] ++ } ++ + if (is_fuchsia) { + # safe-stack does not work with the interpreter. + cflags += [ +diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn +index 14548125acb..2b758ae2db9 100644 +--- a/runtime/bin/BUILD.gn ++++ b/runtime/bin/BUILD.gn +@@ -37,9 +37,6 @@ config("libdart_builtin_config") { + is_linux = false + } + +- print("dart runtime is_linux:" ,is_linux) +- print("dart runtime is_win:" ,is_win) +- print("dart runtime is_ohos:" , is_ohos) + if(is_ohos && !is_win){ + libs +=[ + "hilog_ndk.z" +diff --git a/runtime/bin/builtin_natives.cc b/runtime/bin/builtin_natives.cc +index 5e70d68bfc6..185638b034e 100644 +--- a/runtime/bin/builtin_natives.cc ++++ b/runtime/bin/builtin_natives.cc +@@ -18,11 +18,9 @@ + #include "bin/io_natives.h" + #include "bin/platform.h" + +-#ifdef DART_TARGET_OS_OHOS +-#ifndef DART_HOST_OS_WINDOWS +-//#include +-//#include +-#endif ++#ifdef DART_HOST_OS_OHOS ++#include ++#include + #endif + + namespace dart { +@@ -114,13 +112,11 @@ void FUNCTION_NAME(Builtin_PrintString)(Dart_NativeArguments args) { + ASSERT(res == nullptr); + } + +-#ifdef DART_TARGET_OS_OHOS +-#ifndef DART_HOST_OS_WINDOWS +- // { +- // pthread_t thread = pthread_self(); +- // OH_LOG_Print(LOG_APP,LOG_INFO,LOG_DOMAIN,"XComDartVm","Thread:%{public}lu %{public}s",thread,chars) ; +- // } +-#endif ++#ifdef DART_HOST_OS_OHOS ++ { ++ pthread_t thread = pthread_self(); ++ OH_LOG_Print(LOG_APP,LOG_INFO,LOG_DOMAIN,"XComDartVm","Thread:%{public}lu %{public}s",thread,chars); ++ } + #endif + } + +diff --git a/runtime/bin/security_context_ohos.cc b/runtime/bin/security_context_ohos.cc +index 2ae284f0972..5d801a5b07b 100644 +--- a/runtime/bin/security_context_ohos.cc ++++ b/runtime/bin/security_context_ohos.cc +@@ -30,7 +30,6 @@ const intptr_t SSLCertContext::kApproximateSize = + sizeof(SSLCertContext) + root_certificates_pem_length; + + void SSLCertContext::TrustBuiltinRoots() { +- // First, try to use locations specified on the command line. + if (root_certs_file() != NULL) { + LoadRootCertFile(root_certs_file()); + return; +@@ -40,35 +39,17 @@ void SSLCertContext::TrustBuiltinRoots() { + return; + } + +- if (bypass_trusting_system_roots()) { +- if (SSL_LOG_STATUS) { +- Syslog::Print("Bypass trusting Linux built-in roots\n"); +- } +- } else { +- // On Linux, we use the compiled-in trusted certs as a last resort. First, +- // we try to find the trusted certs in various standard locations. A good +- // discussion of the complexities of this endeavor can be found here: +- // +- // https://www.happyassassin.net/2015/01/12/a-note-about-ssltls-trusted-certificate-stores-and-platforms/ +- const char* bundle = "/etc/pki/tls/certs/ca-bundle.crt"; +- const char* cachedir = "/etc/ssl/certs"; +- if (File::Exists(NULL, bundle)) { +- LoadRootCertFile(bundle); +- return; +- } +- +- if (Directory::Exists(NULL, cachedir) == Directory::EXISTS) { +- LoadRootCertCache(cachedir); +- return; +- } ++ const char* bundle = "/etc/ssl/certs/cacert.pem"; ++ const char* cachedir = "/etc/ssl/certs"; ++ if (File::Exists(NULL, bundle)) { ++ LoadRootCertFile(bundle); ++ return; + } + +- // Fall back on the compiled-in certs if the standard locations don't exist, +- // or we aren't on Linux. +- if (SSL_LOG_STATUS) { +- Syslog::Print("Trusting compiled-in roots\n"); ++ if (Directory::Exists(NULL, cachedir) == Directory::EXISTS) { ++ LoadRootCertCache(cachedir); ++ return; + } +- AddCompiledInCerts(); + } + + void SSLCertContext::RegisterCallbacks(SSL* ssl) { +diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h +index f8a861e6c54..ca859e2b421 100644 +--- a/runtime/platform/globals.h ++++ b/runtime/platform/globals.h +@@ -108,7 +108,7 @@ + // Check for Android first, to determine its difference from Linux. + #define DART_HOST_OS_ANDROID 1 + +-#elif defined(DART_TARGET_OS_OHOS) && !defined(_WIN32) ++#elif defined(DART_TARGET_OS_OHOS) && defined(DART_RUNTIME_OS_OHOS) + + #define DART_HOST_OS_OHOS 1 + +diff --git a/runtime/platform/syslog_ohos.cc b/runtime/platform/syslog_ohos.cc +index 3dcbc463dc6..139881f7271 100644 +--- a/runtime/platform/syslog_ohos.cc ++++ b/runtime/platform/syslog_ohos.cc +@@ -7,20 +7,40 @@ + + #include "platform/syslog.h" + +-#include // NOLINT ++#include // NOLINT ++#include // NOLINT + + namespace dart { ++#define HILOG_LOG_DOMAIN 0x0000 ++#define HILOG_LOG_TAG "Dart" + + void Syslog::VPrint(const char* format, va_list args) { +- vfprintf(stdout, format, args); ++ ++ va_list stdio_args; ++ va_copy(stdio_args, args); ++ vprintf(format, stdio_args); + fflush(stdout); ++ va_end(stdio_args); ++ ++ va_list log_args; ++ va_copy(log_args, args); ++ ((void)OH_LOG_Print(LOG_APP, LOG_INFO, HILOG_LOG_DOMAIN, \ ++ HILOG_LOG_TAG, format, log_args)); ++ va_end(log_args); + } + + void Syslog::VPrintErr(const char* format, va_list args) { +- vfprintf(stderr, format, args); +- fflush(stderr); ++ va_list stdio_args; ++ va_copy(stdio_args, args); ++ vprintf(format, stdio_args); ++ fflush(stdout); ++ va_end(stdio_args); ++ va_list log_args; ++ va_copy(log_args, args); ++ ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, HILOG_LOG_DOMAIN, \ ++ HILOG_LOG_TAG, format, log_args)); ++ va_end(log_args); + } + + } // namespace dart +- + #endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/os_ohos.cc b/runtime/vm/os_ohos.cc +index 7ac0322c01c..2f6b4990a9f 100644 +--- a/runtime/vm/os_ohos.cc ++++ b/runtime/vm/os_ohos.cc +@@ -32,6 +32,7 @@ + #include "vm/timeline.h" + #include "vm/zone.h" + ++#include // NOLINT + namespace dart { + + #ifndef PRODUCT +@@ -565,7 +566,7 @@ DART_NOINLINE uintptr_t OS::GetProgramCounter() { + void OS::Print(const char* format, ...) { + va_list args; + va_start(args, format); +- VFPrint(stdout, format, args); ++ ((void)OH_LOG_Print(LOG_APP, LOG_INFO, 0x0000, "DartVM", format, args)); + va_end(args); + } + +@@ -645,7 +646,7 @@ void OS::RegisterCodeObservers() { + void OS::PrintErr(const char* format, ...) { + va_list args; + va_start(args, format); +- VFPrint(stderr, format, args); ++ ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, 0x0000, "DartVM", format, args)); + va_end(args); + } + diff --git a/attachment/repos/dart.patch4 b/attachment/repos/dart.patch4 new file mode 100644 index 0000000000000000000000000000000000000000..87298d43ef1a9aca25ce459f7a84dc683ad78a38 --- /dev/null +++ b/attachment/repos/dart.patch4 @@ -0,0 +1,53 @@ +diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h +index 92264e535b1..fc254768d62 100644 +--- a/runtime/vm/flag_list.h ++++ b/runtime/vm/flag_list.h +@@ -149,7 +149,7 @@ constexpr bool FLAG_support_il_printer = false; + "Maximum number of polymorphic checks in equality operator,") \ + P(new_gen_semi_max_size, int, kDefaultNewGenSemiMaxSize, \ + "Max size of new gen semi space in MB") \ +- P(new_gen_semi_initial_size, int, (kWordSize <= 4) ? 1 : 2, \ ++ P(new_gen_semi_initial_size, int, (kWordSize <= 4) ? 1 : 32, \ + "Initial size of new gen semi space in MB") \ + P(optimization_counter_threshold, int, kDefaultOptimizationCounterThreshold, \ + "Function's usage-counter value before it is optimized, -1 means never") \ +diff --git a/runtime/vm/globals.h b/runtime/vm/globals.h +index 69c54277b03..c66141aea91 100644 +--- a/runtime/vm/globals.h ++++ b/runtime/vm/globals.h +@@ -60,7 +60,7 @@ const intptr_t kBytesPerBigIntDigit = 4; + // Set the VM limit below the OS limit to increase the likelihood of failing + // gracefully with a Dart OutOfMemory exception instead of SIGABORT. + const intptr_t kDefaultMaxOldGenHeapSize = (kWordSize <= 4) ? 1536 : 30720; +-const intptr_t kDefaultNewGenSemiMaxSize = (kWordSize <= 4) ? 8 : 16; ++const intptr_t kDefaultNewGenSemiMaxSize = (kWordSize <= 4) ? 8 : 256; + + #define kPosInfinity bit_cast(DART_UINT64_C(0x7ff0000000000000)) + #define kNegInfinity bit_cast(DART_UINT64_C(0xfff0000000000000)) +diff --git a/runtime/vm/heap/page.h b/runtime/vm/heap/page.h +index 75de57017b6..9859be5d80a 100644 +--- a/runtime/vm/heap/page.h ++++ b/runtime/vm/heap/page.h +@@ -24,7 +24,7 @@ class UnwindingRecords; + // can be computed by masking the object with kPageMask. This does not apply to + // image pages, whose address is chosen by the system loader rather than the + // Dart VM. +-static constexpr intptr_t kPageSize = 512 * KB; ++static constexpr intptr_t kPageSize = 4 * 1024 * KB; + static constexpr intptr_t kPageSizeInWords = kPageSize / kWordSize; + static constexpr intptr_t kPageMask = ~(kPageSize - 1); + +diff --git a/runtime/vm/heap/scavenger.h b/runtime/vm/heap/scavenger.h +index c61452f91a6..e66049579dd 100644 +--- a/runtime/vm/heap/scavenger.h ++++ b/runtime/vm/heap/scavenger.h +@@ -123,7 +123,7 @@ class ScavengeStats { + + class Scavenger { + private: +- static constexpr intptr_t kTLABSize = 512 * KB; ++ static constexpr intptr_t kTLABSize = 4 * 1024 * KB; + + public: + Scavenger(Heap* heap, intptr_t max_semi_capacity_in_words); +-- \ No newline at end of file diff --git a/attachment/repos/dart.patch5 b/attachment/repos/dart.patch5 new file mode 100644 index 0000000000000000000000000000000000000000..b9293e6d10cf724842a02e71f9f9779e67755671 --- /dev/null +++ b/attachment/repos/dart.patch5 @@ -0,0 +1,15 @@ +diff --git a/runtime/bin/socket_base_posix.cc b/runtime/bin/socket_base_posix.cc +index e10127aa71a..2ce99833887 100644 +--- a/runtime/bin/socket_base_posix.cc ++++ b/runtime/bin/socket_base_posix.cc +@@ -3,7 +3,7 @@ + // BSD-style license that can be found in the LICENSE file. + + #include "platform/globals.h" +-#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) ++#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_OHOS) + #include "bin/socket_base.h" + + #include // NOLINT +-- + diff --git a/attachment/repos/dart.patch6 b/attachment/repos/dart.patch6 new file mode 100644 index 0000000000000000000000000000000000000000..4b8567f3f94ce996eb006eca6137526d3019c975 --- /dev/null +++ b/attachment/repos/dart.patch6 @@ -0,0 +1,30 @@ +diff --git a/runtime/platform/assert.cc b/runtime/platform/assert.cc +index 63aa05f4323..95372422b3c 100644 +--- a/runtime/platform/assert.cc ++++ b/runtime/platform/assert.cc +@@ -8,6 +8,10 @@ + #include "platform/globals.h" + #include "platform/syslog.h" + ++#if defined(DART_HOST_OS_OHOS) ++#include ++#endif // defined(DART_HOST_OS_OHOS) ++ + #if defined(DART_HOST_OS_ANDROID) + extern "C" __attribute__((weak)) void android_set_abort_message(const char*); + #endif // defined(DART_HOST_OS_ANDROID) +@@ -36,6 +40,13 @@ void DynamicAssertionHelper::Print(const char* format, + + // Print the buffer on stderr and/or syslog. + Syslog::PrintErr("%s\n", buffer); ++ ++#if defined(DART_HOST_OS_OHOS) ++ if (will_abort && (&set_fatal_message != nullptr)) { ++ set_fatal_message(buffer); ++ } ++#endif // defined(DART_HOST_OS_OHOS) ++ + #if defined(DART_HOST_OS_ANDROID) + if (will_abort && (&android_set_abort_message != nullptr)) { + android_set_abort_message(buffer); +-- \ No newline at end of file diff --git a/attachment/repos/libcxx.patch b/attachment/repos/libcxx.patch new file mode 100644 index 0000000000000000000000000000000000000000..9a765e3509c2a6e07d6161408f7d32f9ee73661c --- /dev/null +++ b/attachment/repos/libcxx.patch @@ -0,0 +1,51 @@ +diff --git a/include/__config b/include/__config +index 22c2ed7fd..c151bbec9 100644 +--- a/include/__config ++++ b/include/__config +@@ -889,7 +889,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD + defined(__sun__) || \ + defined(__MVS__) || \ + defined(_AIX) || \ +- defined(__EMSCRIPTEN__) ++ defined(__EMSCRIPTEN__) || \ ++ defined(__OHOS_FAMILY__) + // clang-format on + # define _LIBCPP_HAS_THREAD_API_PTHREAD + # elif defined(__Fuchsia__) +@@ -967,7 +968,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD + # endif + + # if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) || \ +- defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) ++ defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) || defined(__OHOS_FAMILY__) + # define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE + # endif + +diff --git a/include/__locale b/include/__locale +index 4450123db..0a429c4a7 100644 +--- a/include/__locale ++++ b/include/__locale +@@ -40,6 +40,9 @@ + #elif defined(__wasi__) + // WASI libc uses musl's locales support. + # include <__support/musl/xlocale.h> ++#elif defined(__LITEOS__) || defined(__OHOS__) ++// LiteOS libc uses musl's locales support. ++# include <__support/musl/xlocale.h> + #elif defined(_LIBCPP_HAS_MUSL_LIBC) + # include <__support/musl/xlocale.h> + #endif +diff --git a/src/include/config_elast.h b/src/include/config_elast.h +index bef26ec50..0e762d276 100644 +--- a/src/include/config_elast.h ++++ b/src/include/config_elast.h +@@ -29,6 +29,8 @@ + // No _LIBCPP_ELAST needed on Fuchsia + #elif defined(__wasi__) + // No _LIBCPP_ELAST needed on WASI ++#elif defined(__OHOS_FAMILY__) ++// No _LIBCPP_ELAST needed on OHOS + #elif defined(__EMSCRIPTEN__) + // No _LIBCPP_ELAST needed on Emscripten + #elif defined(__linux__) || defined(_LIBCPP_HAS_MUSL_LIBC) +-- \ No newline at end of file diff --git a/attachment/repos/libcxxabi.patch b/attachment/repos/libcxxabi.patch new file mode 100644 index 0000000000000000000000000000000000000000..b05b0ba3a1c651d58bc979de568437e90bee7e21 --- /dev/null +++ b/attachment/repos/libcxxabi.patch @@ -0,0 +1,28 @@ +diff --git a/src/abort_message.cpp b/src/abort_message.cpp +index 859a503..b761a0d 100644 +--- a/src/abort_message.cpp ++++ b/src/abort_message.cpp +@@ -11,6 +11,10 @@ + #include + #include "abort_message.h" + ++#if defined(__MUSL_OHOS__) ++extern "C" void set_fatal_message(const char *msg); ++#endif // defined(__MUSL_OHOS__) ++ + #ifdef __BIONIC__ + # include + # if __ANDROID_API__ >= 21 +@@ -42,6 +46,11 @@ void abort_message(const char* format, ...) + } + #endif + ++#if defined(__MUSL_OHOS__) ++ if (&set_fatal_message != nullptr) { ++ set_fatal_message(format); ++ } ++#endif // defined(__MUSL_OHOS__) + // Format the arguments into an allocated buffer. We leak the buffer on + // purpose, since we're about to abort() anyway. + #if defined(_LIBCXXABI_USE_CRASHREPORTER_CLIENT) +-- \ No newline at end of file diff --git a/attachment/repos/libcxxabi.patch2 b/attachment/repos/libcxxabi.patch2 new file mode 100644 index 0000000000000000000000000000000000000000..d1e9324e761b3051fc8fa3ddf4b15861ca85a4f3 --- /dev/null +++ b/attachment/repos/libcxxabi.patch2 @@ -0,0 +1,82 @@ +diff --git a/src/abort_message.cpp b/src/abort_message.cpp +index a4e8451..3a647e3 100644 +--- a/src/abort_message.cpp ++++ b/src/abort_message.cpp +@@ -11,11 +11,7 @@ + #include + #include "abort_message.h" + +-#if defined(__MUSL_OHOS__) +-extern "C" void set_fatal_message(const char *msg); +-#endif // defined(__MUSL_OHOS__) +- +-#ifdef __BIONIC__ ++#if defined(__BIONIC__) && !defined(__OHOS__) + # include + # if __ANDROID_API__ >= 21 + # include +@@ -30,6 +26,10 @@ extern "C" void set_fatal_message(const char *msg); + # define _LIBCXXABI_USE_CRASHREPORTER_CLIENT + #endif + ++#if defined(__OHOS__) ++ extern "C" __attribute__((weak)) void set_fatal_message(const char *msg); ++#endif ++ + void abort_message(const char* format, ...) + { + // Write message to stderr. We do this before formatting into a +@@ -46,11 +46,6 @@ void abort_message(const char* format, ...) + } + #endif + +-#if defined(__MUSL_OHOS__) +- if (&set_fatal_message != nullptr) { +- set_fatal_message(format); +- } +-#endif // defined(__MUSL_OHOS__) + // Format the arguments into an allocated buffer. We leak the buffer on + // purpose, since we're about to abort() anyway. + #if defined(_LIBCXXABI_USE_CRASHREPORTER_CLIENT) +@@ -61,6 +56,18 @@ void abort_message(const char* format, ...) + va_end(list); + + CRSetCrashLogMessage(buffer); ++ ++#elif defined(__OHOS__) ++ char* buffer; ++ va_list list; ++ va_start(list, format); ++ vasprintf(&buffer, format, list); ++ va_end(list); ++ ++ if (&set_fatal_message) { ++ set_fatal_message(buffer); ++ } ++ + #elif defined(__BIONIC__) + char* buffer; + va_list list; +@@ -82,7 +89,7 @@ void abort_message(const char* format, ...) + // (tombstone and/or logcat) in older releases. + __assert2(__FILE__, __LINE__, __func__, buffer); + # endif // __ANDROID_API__ >= 21 +-#endif // __BIONIC__ ++#endif // __BIONIC__ || __OHOS__ + + abort(); + } +diff --git a/src/demangle/ItaniumDemangle.h b/src/demangle/ItaniumDemangle.h +index e3f0c6d..0b22fdb 100644 +--- a/src/demangle/ItaniumDemangle.h ++++ b/src/demangle/ItaniumDemangle.h +@@ -5099,7 +5099,7 @@ template <> + struct FloatData + { + #if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \ +- defined(__wasm__) || defined(__riscv) ++ defined(__wasm__) || defined(__riscv) || defined(__loongarch__) + static const size_t mangled_size = 32; + #elif defined(__arm__) || defined(__mips__) || defined(__hexagon__) + static const size_t mangled_size = 16; +-- \ No newline at end of file diff --git a/attachment/repos/native.patch b/attachment/repos/native.patch new file mode 100644 index 0000000000000000000000000000000000000000..ab802bbcada72aaf8acac9c02e8a1e828bee7684 --- /dev/null +++ b/attachment/repos/native.patch @@ -0,0 +1,129 @@ +diff --git a/pkgs/ffigen/example/libclang-example/custom_import.dart b/pkgs/ffigen/example/libclang-example/custom_import.dart +index dd6196c1..90d60626 100644 +--- a/pkgs/ffigen/example/libclang-example/custom_import.dart ++++ b/pkgs/ffigen/example/libclang-example/custom_import.dart +@@ -13,6 +13,8 @@ import 'dart:ffi'; + Abi.androidArm64: Uint64(), + Abi.androidIA32: Uint32(), + Abi.androidX64: Uint64(), ++ Abi.ohosArm: Uint32(), ++ Abi.ohosArm64: Uint64(), + Abi.fuchsiaArm64: Uint64(), + Abi.fuchsiaX64: Uint64(), + Abi.iosArm: Uint32(), +@@ -41,6 +43,8 @@ final class UintPtr extends AbiSpecificInteger { + Abi.androidArm64: Uint64(), + Abi.androidIA32: Uint32(), + Abi.androidX64: Uint64(), ++ Abi.ohosArm: Uint32(), ++ Abi.ohosArm64: Uint64(), + Abi.fuchsiaArm64: Uint64(), + Abi.fuchsiaX64: Uint64(), + Abi.iosArm: Uint32(), +diff --git a/pkgs/native_assets_cli/lib/src/model/architecture.dart b/pkgs/native_assets_cli/lib/src/model/architecture.dart +index 384eb548..9874d166 100644 +--- a/pkgs/native_assets_cli/lib/src/model/architecture.dart ++++ b/pkgs/native_assets_cli/lib/src/model/architecture.dart +@@ -34,6 +34,8 @@ final class ArchitectureImpl implements Architecture { + Abi.androidIA32: ArchitectureImpl.ia32, + Abi.androidX64: ArchitectureImpl.x64, + Abi.androidRiscv64: ArchitectureImpl.riscv64, ++ Abi.ohosArm: ArchitectureImpl.arm, ++ Abi.ohosArm64: ArchitectureImpl.arm64, + Abi.fuchsiaArm64: ArchitectureImpl.arm64, + Abi.fuchsiaX64: ArchitectureImpl.x64, + Abi.iosArm: ArchitectureImpl.arm, +diff --git a/pkgs/native_assets_cli/lib/src/model/os.dart b/pkgs/native_assets_cli/lib/src/model/os.dart +index 73fd3ea0..e069fe70 100644 +--- a/pkgs/native_assets_cli/lib/src/model/os.dart ++++ b/pkgs/native_assets_cli/lib/src/model/os.dart +@@ -13,6 +13,7 @@ final class OSImpl implements OS { + factory OSImpl.fromAbi(Abi abi) => _abiToOS[abi]!; + + static const OSImpl android = OSImpl._('android'); ++ static const OSImpl ohos = OSImpl._('ohos'); + static const OSImpl fuchsia = OSImpl._('fuchsia'); + static const OSImpl iOS = OSImpl._('ios'); + static const OSImpl linux = OSImpl._('linux'); +@@ -21,6 +22,7 @@ final class OSImpl implements OS { + + static const List values = [ + android, ++ ohos, + fuchsia, + iOS, + linux, +@@ -34,6 +36,8 @@ final class OSImpl implements OS { + Abi.androidIA32: OSImpl.android, + Abi.androidX64: OSImpl.android, + Abi.androidRiscv64: OSImpl.android, ++ Abi.ohosArm: OSImpl.ohos, ++ Abi.ohosArm64: OSImpl.ohos, + Abi.fuchsiaArm64: OSImpl.fuchsia, + Abi.fuchsiaX64: OSImpl.fuchsia, + Abi.iosArm: OSImpl.iOS, +@@ -60,6 +64,10 @@ final class OSImpl implements OS { + ArchitectureImpl.x64, + ArchitectureImpl.riscv64, + }, ++ OSImpl.ohos: { ++ ArchitectureImpl.arm, ++ ArchitectureImpl.arm64, ++ }, + OSImpl.fuchsia: { + ArchitectureImpl.arm64, + ArchitectureImpl.x64, +@@ -130,6 +138,7 @@ final class OSImpl implements OS { + /// The default name prefix for dynamic libraries per [OSImpl]. + static const _dylibPrefix = { + OSImpl.android: 'lib', ++ OSImpl.ohos: 'lib', + OSImpl.fuchsia: 'lib', + OSImpl.iOS: 'lib', + OSImpl.linux: 'lib', +@@ -140,6 +149,7 @@ final class OSImpl implements OS { + /// The default extension for dynamic libraries per [OSImpl]. + static const _dylibExtension = { + OSImpl.android: 'so', ++ OSImpl.ohos: 'so', + OSImpl.fuchsia: 'so', + OSImpl.iOS: 'dylib', + OSImpl.linux: 'so', +@@ -153,6 +163,7 @@ final class OSImpl implements OS { + /// The default extension for static libraries per [OSImpl]. + static const _staticlibExtension = { + OSImpl.android: 'a', ++ OSImpl.ohos: 'a', + OSImpl.fuchsia: 'a', + OSImpl.iOS: 'a', + OSImpl.linux: 'a', +@@ -163,6 +174,7 @@ final class OSImpl implements OS { + /// The default extension for executables per [OSImpl]. + static const _executableExtension = { + OSImpl.android: '', ++ OSImpl.ohos: '', + OSImpl.fuchsia: '', + OSImpl.iOS: '', + OSImpl.linux: '', +diff --git a/pkgs/native_assets_cli/lib/src/model/target.dart b/pkgs/native_assets_cli/lib/src/model/target.dart +index 18cdc977..4f9f3ea4 100644 +--- a/pkgs/native_assets_cli/lib/src/model/target.dart ++++ b/pkgs/native_assets_cli/lib/src/model/target.dart +@@ -53,6 +53,8 @@ final class Target implements Comparable { + static const androidIA32 = Target._(Abi.androidIA32); + static const androidX64 = Target._(Abi.androidX64); + static const androidRiscv64 = Target._(Abi.androidRiscv64); ++ static const ohosArm = Target._(Abi.ohosArm); ++ static const ohosArm64 = Target._(Abi.ohosArm64); + static const fuchsiaArm64 = Target._(Abi.fuchsiaArm64); + static const fuchsiaX64 = Target._(Abi.fuchsiaX64); + static const iOSArm = Target._(Abi.iosArm); +@@ -76,6 +78,8 @@ final class Target implements Comparable { + androidIA32, + androidX64, + androidRiscv64, ++ ohosArm, ++ ohosArm64, + fuchsiaArm64, + fuchsiaX64, + iOSArm, diff --git a/attachment/repos/skia-3.22.patch b/attachment/repos/skia-3.22.patch new file mode 100644 index 0000000000000000000000000000000000000000..446b4ca524c637f7bdc75bf59349a796ca90a3cb --- /dev/null +++ b/attachment/repos/skia-3.22.patch @@ -0,0 +1,3951 @@ +diff --git a/BUILD.gn b/BUILD.gn +index c513271c34..73c378fbf6 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -25,7 +25,8 @@ import("gn/ios.gni") + + # Skia public API, generally provided by :skia. + config("skia_public") { +- include_dirs = [ "." ] ++ include_dirs = [ ".", ++ "//third_part/khronos/", ] + + defines = [ + "SK_CODEC_DECODES_BMP", +@@ -35,11 +36,23 @@ config("skia_public") { + if (is_component_build) { + defines += [ "SKIA_DLL" ] + } +- if (is_fuchsia || is_linux) { ++ if (is_fuchsia || is_linux || is_ohos) { + defines += [ "SK_R32_SHIFT=16" ] + } ++ ++ if( is_ohos && !is_win ) { ++ defines += [ ++ "TARGET_OS_OHOS", ++ "SK_BUILD_FOR_OHOS" ++ ] ++ } ++ if (!skia_enable_gpu) { ++ defines += [ "SK_SUPPORT_GPU=0" ] ++ } + if (skia_enable_optimize_size) { +- defines += [ "SK_ENABLE_OPTIMIZE_SIZE" ] ++ defines += [ ++ "SK_ENABLE_OPTIMIZE_SIZE", ++ ] + } + if (skia_enable_precompile) { + defines += [ "SK_ENABLE_PRECOMPILE" ] +@@ -316,6 +329,46 @@ optional("fontmgr_android") { + sources_for_tests = [ "tests/FontMgrAndroidParserTest.cpp" ] + } + ++optional("fontmgr_ohos") { ++ enabled = skia_enable_fontmgr_ohos ++ deps = [ ++ ":typeface_freetype", ++ "//third_party/expat", ++ "//third_party/jsoncpp:jsoncpp", ++ ] ++ public = [ ++ "src/ports/skia_ohos/FontConfig_ohos.h", ++ "src/ports/skia_ohos/FontInfo_ohos.h", ++ "src/ports/skia_ohos/SkFontMgr_ohos.h", ++ "src/ports/skia_ohos/SkFontStyleSet_ohos.h", ++ "src/ports/skia_ohos/SkTypeface_ohos.h", ++ ] ++ sources = [ ++ "src/ports/skia_ohos/FontConfig_ohos.cpp", ++ "src/ports/skia_ohos/SkFontMgr_ohos.cpp", ++ "src/ports/skia_ohos/SkFontStyleSet_ohos.cpp", ++ "src/ports/skia_ohos/SkTypeface_ohos.cpp", ++ "//third_party/vulkan-deps/spirv-headers/src/tools/buildHeaders/jsoncpp/dist/jsoncpp.cpp", ++ ] ++ include_dirs = [ ++ "//third_party/skia/include/private", ++ "//third_party/skia/include/private/base", ++ "//third_party/skia/include/ports", ++ "//third_party/skia/include/core", ++ "//third_party/skia/src/core", ++ "//third_party/skia/src/ports", ++ "//third_party/skia/src/ports/skia_ohos", ++ "//third_party/vulkan-deps/spirv-headers/src/tools/buildHeaders/jsoncpp/dist/", ++ "//third_party/skia/src/core/SkFontDescriptor.h" ++ ] ++} ++ ++optional("fontmgr_ohos_factory") { ++ enabled = skia_enable_fontmgr_ohos ++ deps = [ ":fontmgr_ohos" ] ++ sources = [ "src/ports/skia_ohos/SkFontMgr_ohos_factory.cpp" ] ++} ++ + optional("fontmgr_custom") { + enabled = + skia_enable_fontmgr_custom_directory || +@@ -593,7 +646,7 @@ if (skia_compile_modules) { + sources += [ "tools/SkGetExecutablePath_win.cpp" ] + } else if (is_mac || is_ios) { + sources += [ "tools/SkGetExecutablePath_mac.cpp" ] +- } else if (is_linux || is_android) { ++ } else if (is_linux || is_android || is_ohos) { + sources += [ "tools/SkGetExecutablePath_linux.cpp" ] + } + if (is_win) { +@@ -721,7 +774,7 @@ if (skia_compile_sksl_tests) { + sources += [ "tools/SkGetExecutablePath_win.cpp" ] + } else if (is_mac || is_ios) { + sources += [ "tools/SkGetExecutablePath_mac.cpp" ] +- } else if (is_linux || is_android) { ++ } else if (is_linux || is_android || is_ohos) { + sources += [ "tools/SkGetExecutablePath_linux.cpp" ] + } + if (is_win) { +@@ -994,7 +1047,7 @@ optional("gpu") { + } + } else if (skia_use_webgl) { + sources += [ "src/gpu/ganesh/gl/webgl/GrGLMakeNativeInterface_webgl.cpp" ] +- } else if (is_linux && skia_use_x11) { ++ } else if ((is_linux || is_ohos) && skia_use_x11) { + sources += [ + "src/gpu/ganesh/gl/glx/GrGLMakeGLXInterface.cpp", + "src/gpu/ganesh/gl/glx/GrGLMakeNativeInterface_glx.cpp", +@@ -1009,8 +1062,16 @@ optional("gpu") { + if (target_cpu != "arm64") { + libs += [ "OpenGL32.lib" ] + } +- } else { +- sources += [ "src/gpu/ganesh/gl/GrGLMakeNativeInterface_none.cpp" ] ++ } else if( is_ohos){ ++ libs += [ "EGL" ,"GLESv3"] ++ sources += [ ++ "src/gpu/ganesh/gl/egl/GrGLMakeEGLInterface.cpp", ++ "src/gpu/ganesh/gl/egl/GrGLMakeNativeInterface_egl.cpp", ++ ] ++ }else { ++ sources += [ ++ "src/gpu/ganesh/gl/GrGLMakeNativeInterface_none.cpp", ++ ] + } + public += skia_gpu_gl_public + sources += skia_gpu_gl_private +@@ -1454,6 +1515,7 @@ skia_component("skia") { + ":fontmgr_fuchsia", + ":fontmgr_mac_ct", + ":fontmgr_win", ++ ":fontmgr_ohos", + ":fontmgr_win_gdi", + ":gpu", + ":graphite", +@@ -1545,6 +1607,7 @@ skia_component("skia") { + libs += [ + "Ole32.lib", + "OleAut32.lib", ++ "OpenGL32.lib", + ] + + if (!skia_enable_winuwp) { +@@ -1575,6 +1638,15 @@ skia_component("skia") { + ] + } + ++ if (is_ohos) { ++ sources += [ "src/ports/SkDebug_ohos.cpp" ] ++ libs += [ ++ "EGL", ++ "GLESv3", ++ "hilog_ndk.z" ++ ] ++ } ++ + if (is_linux || is_wasm) { + sources += [ "src/ports/SkDebug_stdio.cpp" ] + if (skia_use_egl) { +@@ -1992,7 +2064,7 @@ if (skia_enable_tools) { + if (is_android || skia_use_egl) { + sources += [ "tools/gpu/gl/egl/CreatePlatformGLTestContext_egl.cpp" ] + libs += [ "EGL" ] +- } else if (is_linux) { ++ } else if (is_linux || is_ohos) { + sources += [ "tools/gpu/gl/glx/CreatePlatformGLTestContext_glx.cpp" ] + libs += [ + "GLU", +@@ -2357,7 +2429,7 @@ if (skia_enable_tools) { + ] + } + +- if (is_linux || is_mac || skia_enable_optimize_size) { ++ if (is_linux || is_mac || skia_enable_optimize_size || is_ohos) { + if (skia_enable_skottie) { + test_app("skottie_tool") { + deps = [ "modules/skottie:tool" ] +@@ -2802,7 +2874,7 @@ if (skia_enable_tools) { + "tools/sk_app/android/surface_glue_android.h", + ] + libs += [ "android" ] +- } else if (is_linux) { ++ } else if (is_linux || is_ohos) { + sources += [ + "tools/SkGetExecutablePath_linux.cpp", + "tools/sk_app/unix/Window_unix.cpp", +@@ -2844,6 +2916,103 @@ if (skia_enable_tools) { + frameworks += [ "QuartzCore.framework" ] + } + ++ if (skia_use_gl) { ++ sources += [ "tools/sk_app/GLWindowContext.cpp" ] ++ sources += [ "tools/sk_app/GLWindowContext.h" ] ++ if (is_android) { ++ sources += [ "tools/sk_app/android/GLWindowContext_android.cpp" ] ++ } else if (is_linux || is_ohos) { ++ sources += [ "tools/sk_app/unix/GLWindowContext_unix.cpp" ] ++ } else if (is_win) { ++ sources += [ "tools/sk_app/win/GLWindowContext_win.cpp" ] ++ if (skia_use_angle) { ++ sources += [ "tools/sk_app/win/ANGLEWindowContext_win.cpp" ] ++ } ++ } else if (is_mac) { ++ sources += [ ++ "tools/sk_app/mac/GLWindowContext_mac.mm", ++ "tools/sk_app/mac/RasterWindowContext_mac.mm", ++ ] ++ } else if (is_ios) { ++ sources += [ ++ "tools/sk_app/ios/GLWindowContext_ios.mm", ++ "tools/sk_app/ios/RasterWindowContext_ios.mm", ++ ] ++ } ++ } ++ ++ if (skia_use_vulkan) { ++ sources += [ "tools/sk_app/VulkanWindowContext.cpp" ] ++ sources += [ "tools/sk_app/VulkanWindowContext.h" ] ++ if (is_android) { ++ sources += [ "tools/sk_app/android/VulkanWindowContext_android.cpp" ] ++ } else if (is_linux || is_ohos) { ++ sources += [ "tools/sk_app/unix/VulkanWindowContext_unix.cpp" ] ++ libs += [ "X11-xcb" ] ++ } else if (is_win) { ++ sources += [ "tools/sk_app/win/VulkanWindowContext_win.cpp" ] ++ } ++ } ++ ++ if (skia_use_metal) { ++ sources += [ "tools/sk_app/MetalWindowContext.mm" ] ++ sources += [ "tools/sk_app/MetalWindowContext.h" ] ++ if (skia_enable_graphite) { ++ sources += [ "tools/sk_app/GraphiteMetalWindowContext.mm" ] ++ sources += [ "tools/sk_app/GraphiteMetalWindowContext.h" ] ++ } ++ if (is_mac) { ++ sources += [ "tools/sk_app/mac/MetalWindowContext_mac.mm" ] ++ if (skia_enable_graphite) { ++ sources += [ "tools/sk_app/mac/GraphiteMetalWindowContext_mac.mm" ] ++ } ++ } else if (is_ios) { ++ sources += [ "tools/sk_app/ios/MetalWindowContext_ios.mm" ] ++ } ++ } ++ ++ if (skia_use_direct3d) { ++ sources += [ "tools/sk_app/win/D3D12WindowContext_win.cpp" ] ++ } ++ ++ if (skia_use_dawn) { ++ sources += [ "tools/sk_app/DawnWindowContext.cpp" ] ++ sources += [ "tools/sk_app/DawnWindowContext.h" ] ++ if (is_linux || is_ohos) { ++ if (dawn_enable_vulkan) { ++ sources += [ "tools/sk_app/unix/DawnVulkanWindowContext_unix.cpp" ] ++ defines = [ "VK_USE_PLATFORM_XCB_KHR" ] ++ libs += [ "X11-xcb" ] ++ if (skia_enable_graphite) { ++ sources += ++ [ "tools/sk_app/unix/GraphiteDawnVulkanWindowContext_unix.cpp" ] ++ sources += [ "tools/sk_app/GraphiteDawnWindowContext.cpp" ] ++ sources += [ "tools/sk_app/GraphiteDawnWindowContext.h" ] ++ } ++ } ++ } else if (is_win) { ++ if (dawn_enable_d3d12) { ++ sources += [ "tools/sk_app/win/DawnD3D12WindowContext_win.cpp" ] ++ if (skia_enable_graphite) { ++ sources += ++ [ "tools/sk_app/win/GraphiteDawnD3D12WindowContext_win.cpp" ] ++ sources += [ "tools/sk_app/GraphiteDawnWindowContext.cpp" ] ++ sources += [ "tools/sk_app/GraphiteDawnWindowContext.h" ] ++ } ++ } ++ } else if (is_mac) { ++ if (dawn_enable_metal) { ++ sources += [ "tools/sk_app/mac/DawnMTLWindowContext_mac.mm" ] ++ if (skia_enable_graphite) { ++ sources += ++ [ "tools/sk_app/mac/GraphiteDawnMetalWindowContext_mac.mm" ] ++ sources += [ "tools/sk_app/GraphiteDawnWindowContext.cpp" ] ++ sources += [ "tools/sk_app/GraphiteDawnWindowContext.h" ] ++ } ++ } ++ } ++ } ++ + deps = [ + ":tool_utils", + "tools/window", +@@ -2856,7 +3025,7 @@ if (skia_enable_tools) { + } + } + +- if (!skia_use_vulkan && (is_mac || is_linux || is_win)) { ++ if (!skia_use_vulkan && (is_mac || is_linux || is_win || is_ohos)) { + test_app("fiddle_examples") { + sources = [ + "tools/fiddle/all_examples.cpp", +@@ -3010,7 +3179,7 @@ if (skia_enable_tools) { + } + } + +- if (skia_use_gl && !skia_use_angle && (is_linux || is_win || is_mac)) { ++ if (skia_use_gl && !skia_use_angle && (is_linux || is_win || is_mac || is_ohos)) { + test_app("HelloWorld") { + sources = [ "example/HelloWorld.cpp" ] + libs = [] +diff --git a/gn/BUILDCONFIG.gn b/gn/BUILDCONFIG.gn +index f17c7d8d2f..c7522b9d5a 100644 +--- a/gn/BUILDCONFIG.gn ++++ b/gn/BUILDCONFIG.gn +@@ -70,6 +70,7 @@ is_linux = current_os == "linux" + is_mac = current_os == "mac" + is_wasm = current_os == "wasm" + is_win = current_os == "win" ++is_ohos = current_os == "ohos" + + # This is just to make the Dawn build files happy. Skia itself uses target_os = "linux" + # for ChromeOS, so this variable will not affect Skia proper. +diff --git a/gn/skia.gni b/gn/skia.gni +index 769d3240bd..623869386d 100644 +--- a/gn/skia.gni ++++ b/gn/skia.gni +@@ -20,6 +20,8 @@ declare_args() { + skia_enable_fontmgr_fuchsia = is_fuchsia + skia_enable_fontmgr_win = is_win + skia_enable_gpu_debug_layers = is_skia_dev_build && is_debug ++ skia_enable_fontmgr_ohos = is_ohos ++ skia_enable_gpu = true + skia_enable_optimize_size = false + skia_enable_pdf = !is_wasm + skia_enable_precompile = true +@@ -46,10 +48,10 @@ declare_args() { + skia_use_expat = !is_wasm + skia_use_ffmpeg = false + skia_use_fixed_gamma_text = is_android +- skia_use_fontconfig = is_linux ++ skia_use_fontconfig = is_linux || is_ohos + skia_use_fontations = false + skia_use_fonthost_mac = is_mac || is_ios +- skia_use_freetype = is_android || is_fuchsia || is_linux || is_wasm ++ skia_use_freetype = is_android || is_fuchsia || is_linux || is_wasm || is_ohos + skia_use_harfbuzz = true + skia_use_gl = !is_fuchsia + skia_use_icu = !is_fuchsia +@@ -71,7 +73,7 @@ declare_args() { + skia_use_lua = is_skia_dev_build && !is_ios + skia_use_metal = false + skia_use_ndk_images = is_android && defined(ndk_api) && ndk_api >= 30 +- skia_use_perfetto = is_linux || is_mac || is_android ++ skia_use_perfetto = is_linux || is_mac || is_android || is_ohos + + # Currently only supported in Android framework + skia_android_framework_use_perfetto = false +@@ -80,7 +82,7 @@ declare_args() { + skia_use_webgl = is_wasm + skia_use_webgpu = is_wasm + skia_use_wuffs = true +- skia_use_x11 = is_linux ++ skia_use_x11 = is_linux || is_ohos + skia_use_xps = true + + # Use the safe mode for libcxx +@@ -144,6 +146,8 @@ declare_args() { + } + + declare_args() { ++# skia_enable_fontmgr_ohos = is_ohos ++ skia_use_freetype2 = true + skia_enable_fontmgr_android = skia_use_expat && skia_use_freetype + skia_enable_fontmgr_custom_directory = + skia_use_freetype && !is_fuchsia && !is_wasm +@@ -169,6 +173,33 @@ declare_args() { + + # libgrapheme sources + skia_libgrapheme_third_party_dir = "//third_party/libgrapheme" ++ } ++ ++declare_args() { ++ # skia_fontmgr_factory should define SkFontMgr::Factory() ++ if (is_ohos) { ++ skia_fontmgr_factory = ":fontmgr_ohos_factory" ++ } else if (skia_enable_fontmgr_empty) { ++ skia_fontmgr_factory = ":fontmgr_empty_factory" ++ } else if (is_android && skia_enable_fontmgr_android) { ++ skia_fontmgr_factory = ":fontmgr_android_factory" ++ } else if (is_win && skia_enable_fontmgr_win) { ++ skia_fontmgr_factory = ":fontmgr_win_factory" ++ } else if ((is_mac || is_ios) && skia_use_fonthost_mac) { ++ skia_fontmgr_factory = ":fontmgr_mac_ct_factory" ++ } else if (skia_enable_fontmgr_fontconfig) { ++ skia_fontmgr_factory = ":fontmgr_fontconfig_factory" ++ } else if (skia_enable_fontmgr_custom_directory) { ++ skia_fontmgr_factory = ":fontmgr_custom_directory_factory" ++ } else if (skia_enable_fontmgr_custom_embedded) { ++ skia_fontmgr_factory = ":fontmgr_custom_embedded_factory" ++ } else if (skia_enable_fontmgr_custom_empty) { ++ skia_fontmgr_factory = ":fontmgr_custom_empty_factory" ++ } else { ++ #"src/ports/SkFontMgr_FontConfigInterface_factory.cpp" #WontFix ++ #"src/ports/SkFontMgr_win_gdi_factory.cpp" # WontFix ++ skia_fontmgr_factory = ":fontmgr_empty_factory" ++ } + } + + assert(!skia_use_dawn || skia_enable_graphite) # Dawn is Graphite-only +diff --git a/gn/skia/BUILD.gn b/gn/skia/BUILD.gn +index fc5c2ad4de..f82fdc36bd 100644 +--- a/gn/skia/BUILD.gn ++++ b/gn/skia/BUILD.gn +@@ -256,7 +256,7 @@ config("default") { + } + } + +- if (is_linux) { ++ if (is_linux || is_ohos) { + libs += [ "pthread" ] + } + +@@ -372,7 +372,7 @@ config("default") { + ldflags += [ "-fsanitize=$sanitizers" ] + } + +- if (is_linux) { ++ if (is_linux || is_ohos) { + cflags_cc += [ "-stdlib=libc++" ] + ldflags += [ "-stdlib=libc++" ] + } +@@ -749,7 +749,7 @@ config("executable") { + ] + } else if (is_mac) { + ldflags = [ "-Wl,-rpath,@loader_path/." ] +- } else if (is_linux) { ++ } else if (is_linux || is_ohos) { + ldflags = [ + "-rdynamic", + "-Wl,-rpath,\$ORIGIN", +diff --git a/include/ports/SkFontMgr_ohos_api.h b/include/ports/SkFontMgr_ohos_api.h +new file mode 100644 +index 0000000000..4199b4ad96 +--- /dev/null ++++ b/include/ports/SkFontMgr_ohos_api.h +@@ -0,0 +1,12 @@ ++#ifndef SkFontMgr_ohos_api_DEFINED ++#define SkFontMgr_ohos_api_DEFINED ++ ++#include "include/core/SkRefCnt.h" ++ ++class SkFontMgr; ++class SkFontScanner; ++ ++SK_API sk_sp SkFontMgr_New_OHOS(const char* path); ++SK_API sk_sp SkFontMgr_New_OHOS(); ++ ++#endif // SkFontMgr_ohos_api_DEFINED +diff --git a/src/core/SkRRectPriv.h b/src/core/SkRRectPriv.h +index cea579c901..baaeaff966 100644 +--- a/src/core/SkRRectPriv.h ++++ b/src/core/SkRRectPriv.h +@@ -20,7 +20,7 @@ public: + } + + static SkVector GetSimpleRadii(const SkRRect& rr) { +- SkASSERT(!rr.isComplex()); ++ // SkASSERT(!rr.isComplex()); + return rr.fRadii[0]; + } + +diff --git a/src/gpu/ganesh/gl/egl/gl2.h b/src/gpu/ganesh/gl/egl/gl2.h +new file mode 100644 +index 0000000000..176023660e +--- /dev/null ++++ b/src/gpu/ganesh/gl/egl/gl2.h +@@ -0,0 +1,678 @@ ++#ifndef __gles2_gl2_h_ ++#define __gles2_gl2_h_ 1 ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* ++** Copyright (c) 2013-2018 The Khronos Group Inc. ++** ++** Permission is hereby granted, free of charge, to any person obtaining a ++** copy of this software and/or associated documentation files (the ++** "Materials"), to deal in the Materials without restriction, including ++** without limitation the rights to use, copy, modify, merge, publish, ++** distribute, sublicense, and/or sell copies of the Materials, and to ++** permit persons to whom the Materials are furnished to do so, subject to ++** the following conditions: ++** ++** The above copyright notice and this permission notice shall be included ++** in all copies or substantial portions of the Materials. ++** ++** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ++** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ++** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ++** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ++** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. ++*/ ++/* ++** This header is generated from the Khronos OpenGL / OpenGL ES XML ++** API Registry. The current version of the Registry, generator scripts ++** used to make the header, and the header can be found at ++** https://github.com/KhronosGroup/OpenGL-Registry ++*/ ++ ++/* Chromium-specific GLES2 declarations */ ++// #include ++ ++#include "gl2platform.h" ++ ++#ifndef GL_APIENTRYP ++#define GL_APIENTRYP GL_APIENTRY* ++#endif ++ ++#ifndef GL_GLES_PROTOTYPES ++#define GL_GLES_PROTOTYPES 1 ++#endif ++ ++/* Generated on date 20190911 */ ++ ++/* Generated C header for: ++ * API: gles2 ++ * Profile: common ++ * Versions considered: 2\.[0-9] ++ * Versions emitted: .* ++ * Default extensions included: None ++ * Additional extensions included: _nomatch_^ ++ * Extensions removed: _nomatch_^ ++ */ ++ ++#ifndef GL_ES_VERSION_2_0 ++#define GL_ES_VERSION_2_0 1 ++#include ++typedef khronos_int8_t GLbyte; ++typedef khronos_float_t GLclampf; ++typedef khronos_int32_t GLfixed; ++typedef khronos_int16_t GLshort; ++typedef khronos_uint16_t GLushort; ++typedef void GLvoid; ++typedef struct __GLsync *GLsync; ++typedef khronos_int64_t GLint64; ++typedef khronos_uint64_t GLuint64; ++typedef unsigned int GLenum; ++typedef unsigned int GLuint; ++typedef char GLchar; ++typedef khronos_float_t GLfloat; ++typedef khronos_ssize_t GLsizeiptr; ++typedef khronos_intptr_t GLintptr; ++typedef unsigned int GLbitfield; ++typedef int GLint; ++typedef unsigned char GLboolean; ++typedef int GLsizei; ++typedef khronos_uint8_t GLubyte; ++#define GL_DEPTH_BUFFER_BIT 0x00000100 ++#define GL_STENCIL_BUFFER_BIT 0x00000400 ++#define GL_COLOR_BUFFER_BIT 0x00004000 ++#define GL_FALSE 0 ++#define GL_TRUE 1 ++#define GL_POINTS 0x0000 ++#define GL_LINES 0x0001 ++#define GL_LINE_LOOP 0x0002 ++#define GL_LINE_STRIP 0x0003 ++#define GL_TRIANGLES 0x0004 ++#define GL_TRIANGLE_STRIP 0x0005 ++#define GL_TRIANGLE_FAN 0x0006 ++#define GL_ZERO 0 ++#define GL_ONE 1 ++#define GL_SRC_COLOR 0x0300 ++#define GL_ONE_MINUS_SRC_COLOR 0x0301 ++#define GL_SRC_ALPHA 0x0302 ++#define GL_ONE_MINUS_SRC_ALPHA 0x0303 ++#define GL_DST_ALPHA 0x0304 ++#define GL_ONE_MINUS_DST_ALPHA 0x0305 ++#define GL_DST_COLOR 0x0306 ++#define GL_ONE_MINUS_DST_COLOR 0x0307 ++#define GL_SRC_ALPHA_SATURATE 0x0308 ++#define GL_FUNC_ADD 0x8006 ++#define GL_BLEND_EQUATION 0x8009 ++#define GL_BLEND_EQUATION_RGB 0x8009 ++#define GL_BLEND_EQUATION_ALPHA 0x883D ++#define GL_FUNC_SUBTRACT 0x800A ++#define GL_FUNC_REVERSE_SUBTRACT 0x800B ++#define GL_BLEND_DST_RGB 0x80C8 ++#define GL_BLEND_SRC_RGB 0x80C9 ++#define GL_BLEND_DST_ALPHA 0x80CA ++#define GL_BLEND_SRC_ALPHA 0x80CB ++#define GL_CONSTANT_COLOR 0x8001 ++#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 ++#define GL_CONSTANT_ALPHA 0x8003 ++#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 ++#define GL_BLEND_COLOR 0x8005 ++#define GL_ARRAY_BUFFER 0x8892 ++#define GL_ELEMENT_ARRAY_BUFFER 0x8893 ++#define GL_ARRAY_BUFFER_BINDING 0x8894 ++#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 ++#define GL_STREAM_DRAW 0x88E0 ++#define GL_STATIC_DRAW 0x88E4 ++#define GL_DYNAMIC_DRAW 0x88E8 ++#define GL_BUFFER_SIZE 0x8764 ++#define GL_BUFFER_USAGE 0x8765 ++#define GL_CURRENT_VERTEX_ATTRIB 0x8626 ++#define GL_FRONT 0x0404 ++#define GL_BACK 0x0405 ++#define GL_FRONT_AND_BACK 0x0408 ++#define GL_TEXTURE_2D 0x0DE1 ++#define GL_CULL_FACE 0x0B44 ++#define GL_BLEND 0x0BE2 ++#define GL_DITHER 0x0BD0 ++#define GL_STENCIL_TEST 0x0B90 ++#define GL_DEPTH_TEST 0x0B71 ++#define GL_SCISSOR_TEST 0x0C11 ++#define GL_POLYGON_OFFSET_FILL 0x8037 ++#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E ++#define GL_SAMPLE_COVERAGE 0x80A0 ++#define GL_NO_ERROR 0 ++#define GL_INVALID_ENUM 0x0500 ++#define GL_INVALID_VALUE 0x0501 ++#define GL_INVALID_OPERATION 0x0502 ++#define GL_OUT_OF_MEMORY 0x0505 ++#define GL_CW 0x0900 ++#define GL_CCW 0x0901 ++#define GL_LINE_WIDTH 0x0B21 ++#define GL_ALIASED_POINT_SIZE_RANGE 0x846D ++#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E ++#define GL_CULL_FACE_MODE 0x0B45 ++#define GL_FRONT_FACE 0x0B46 ++#define GL_DEPTH_RANGE 0x0B70 ++#define GL_DEPTH_WRITEMASK 0x0B72 ++#define GL_DEPTH_CLEAR_VALUE 0x0B73 ++#define GL_DEPTH_FUNC 0x0B74 ++#define GL_STENCIL_CLEAR_VALUE 0x0B91 ++#define GL_STENCIL_FUNC 0x0B92 ++#define GL_STENCIL_FAIL 0x0B94 ++#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 ++#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 ++#define GL_STENCIL_REF 0x0B97 ++#define GL_STENCIL_VALUE_MASK 0x0B93 ++#define GL_STENCIL_WRITEMASK 0x0B98 ++#define GL_STENCIL_BACK_FUNC 0x8800 ++#define GL_STENCIL_BACK_FAIL 0x8801 ++#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 ++#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 ++#define GL_STENCIL_BACK_REF 0x8CA3 ++#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 ++#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 ++#define GL_VIEWPORT 0x0BA2 ++#define GL_SCISSOR_BOX 0x0C10 ++#define GL_COLOR_CLEAR_VALUE 0x0C22 ++#define GL_COLOR_WRITEMASK 0x0C23 ++#define GL_UNPACK_ALIGNMENT 0x0CF5 ++#define GL_PACK_ALIGNMENT 0x0D05 ++#define GL_MAX_TEXTURE_SIZE 0x0D33 ++#define GL_MAX_VIEWPORT_DIMS 0x0D3A ++#define GL_SUBPIXEL_BITS 0x0D50 ++#define GL_RED_BITS 0x0D52 ++#define GL_GREEN_BITS 0x0D53 ++#define GL_BLUE_BITS 0x0D54 ++#define GL_ALPHA_BITS 0x0D55 ++#define GL_DEPTH_BITS 0x0D56 ++#define GL_STENCIL_BITS 0x0D57 ++#define GL_POLYGON_OFFSET_UNITS 0x2A00 ++#define GL_POLYGON_OFFSET_FACTOR 0x8038 ++#define GL_TEXTURE_BINDING_2D 0x8069 ++#define GL_SAMPLE_BUFFERS 0x80A8 ++#define GL_SAMPLES 0x80A9 ++#define GL_SAMPLE_COVERAGE_VALUE 0x80AA ++#define GL_SAMPLE_COVERAGE_INVERT 0x80AB ++#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 ++#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 ++#define GL_DONT_CARE 0x1100 ++#define GL_FASTEST 0x1101 ++#define GL_NICEST 0x1102 ++#define GL_GENERATE_MIPMAP_HINT 0x8192 ++#define GL_BYTE 0x1400 ++#define GL_UNSIGNED_BYTE 0x1401 ++#define GL_SHORT 0x1402 ++#define GL_UNSIGNED_SHORT 0x1403 ++#define GL_INT 0x1404 ++#define GL_UNSIGNED_INT 0x1405 ++#define GL_FLOAT 0x1406 ++#define GL_FIXED 0x140C ++#define GL_DEPTH_COMPONENT 0x1902 ++#define GL_ALPHA 0x1906 ++#define GL_RGB 0x1907 ++#define GL_RGBA 0x1908 ++#define GL_LUMINANCE 0x1909 ++#define GL_LUMINANCE_ALPHA 0x190A ++#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 ++#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 ++#define GL_UNSIGNED_SHORT_5_6_5 0x8363 ++#define GL_FRAGMENT_SHADER 0x8B30 ++#define GL_VERTEX_SHADER 0x8B31 ++#define GL_MAX_VERTEX_ATTRIBS 0x8869 ++#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB ++#define GL_MAX_VARYING_VECTORS 0x8DFC ++#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D ++#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C ++#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 ++#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD ++#define GL_SHADER_TYPE 0x8B4F ++#define GL_DELETE_STATUS 0x8B80 ++#define GL_LINK_STATUS 0x8B82 ++#define GL_VALIDATE_STATUS 0x8B83 ++#define GL_ATTACHED_SHADERS 0x8B85 ++#define GL_ACTIVE_UNIFORMS 0x8B86 ++#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 ++#define GL_ACTIVE_ATTRIBUTES 0x8B89 ++#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A ++#define GL_SHADING_LANGUAGE_VERSION 0x8B8C ++#define GL_CURRENT_PROGRAM 0x8B8D ++#define GL_NEVER 0x0200 ++#define GL_LESS 0x0201 ++#define GL_EQUAL 0x0202 ++#define GL_LEQUAL 0x0203 ++#define GL_GREATER 0x0204 ++#define GL_NOTEQUAL 0x0205 ++#define GL_GEQUAL 0x0206 ++#define GL_ALWAYS 0x0207 ++#define GL_KEEP 0x1E00 ++#define GL_REPLACE 0x1E01 ++#define GL_INCR 0x1E02 ++#define GL_DECR 0x1E03 ++#define GL_INVERT 0x150A ++#define GL_INCR_WRAP 0x8507 ++#define GL_DECR_WRAP 0x8508 ++#define GL_VENDOR 0x1F00 ++#define GL_RENDERER 0x1F01 ++#define GL_VERSION 0x1F02 ++#define GL_EXTENSIONS 0x1F03 ++#define GL_NEAREST 0x2600 ++#define GL_LINEAR 0x2601 ++#define GL_NEAREST_MIPMAP_NEAREST 0x2700 ++#define GL_LINEAR_MIPMAP_NEAREST 0x2701 ++#define GL_NEAREST_MIPMAP_LINEAR 0x2702 ++#define GL_LINEAR_MIPMAP_LINEAR 0x2703 ++#define GL_TEXTURE_MAG_FILTER 0x2800 ++#define GL_TEXTURE_MIN_FILTER 0x2801 ++#define GL_TEXTURE_WRAP_S 0x2802 ++#define GL_TEXTURE_WRAP_T 0x2803 ++#define GL_TEXTURE 0x1702 ++#define GL_TEXTURE_CUBE_MAP 0x8513 ++#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 ++#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 ++#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 ++#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 ++#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 ++#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 ++#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A ++#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C ++#define GL_TEXTURE0 0x84C0 ++#define GL_TEXTURE1 0x84C1 ++#define GL_TEXTURE2 0x84C2 ++#define GL_TEXTURE3 0x84C3 ++#define GL_TEXTURE4 0x84C4 ++#define GL_TEXTURE5 0x84C5 ++#define GL_TEXTURE6 0x84C6 ++#define GL_TEXTURE7 0x84C7 ++#define GL_TEXTURE8 0x84C8 ++#define GL_TEXTURE9 0x84C9 ++#define GL_TEXTURE10 0x84CA ++#define GL_TEXTURE11 0x84CB ++#define GL_TEXTURE12 0x84CC ++#define GL_TEXTURE13 0x84CD ++#define GL_TEXTURE14 0x84CE ++#define GL_TEXTURE15 0x84CF ++#define GL_TEXTURE16 0x84D0 ++#define GL_TEXTURE17 0x84D1 ++#define GL_TEXTURE18 0x84D2 ++#define GL_TEXTURE19 0x84D3 ++#define GL_TEXTURE20 0x84D4 ++#define GL_TEXTURE21 0x84D5 ++#define GL_TEXTURE22 0x84D6 ++#define GL_TEXTURE23 0x84D7 ++#define GL_TEXTURE24 0x84D8 ++#define GL_TEXTURE25 0x84D9 ++#define GL_TEXTURE26 0x84DA ++#define GL_TEXTURE27 0x84DB ++#define GL_TEXTURE28 0x84DC ++#define GL_TEXTURE29 0x84DD ++#define GL_TEXTURE30 0x84DE ++#define GL_TEXTURE31 0x84DF ++#define GL_ACTIVE_TEXTURE 0x84E0 ++#define GL_REPEAT 0x2901 ++#define GL_CLAMP_TO_EDGE 0x812F ++#define GL_MIRRORED_REPEAT 0x8370 ++#define GL_FLOAT_VEC2 0x8B50 ++#define GL_FLOAT_VEC3 0x8B51 ++#define GL_FLOAT_VEC4 0x8B52 ++#define GL_INT_VEC2 0x8B53 ++#define GL_INT_VEC3 0x8B54 ++#define GL_INT_VEC4 0x8B55 ++#define GL_BOOL 0x8B56 ++#define GL_BOOL_VEC2 0x8B57 ++#define GL_BOOL_VEC3 0x8B58 ++#define GL_BOOL_VEC4 0x8B59 ++#define GL_FLOAT_MAT2 0x8B5A ++#define GL_FLOAT_MAT3 0x8B5B ++#define GL_FLOAT_MAT4 0x8B5C ++#define GL_SAMPLER_2D 0x8B5E ++#define GL_SAMPLER_CUBE 0x8B60 ++#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 ++#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 ++#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 ++#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 ++#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A ++#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 ++#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F ++#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A ++#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B ++#define GL_COMPILE_STATUS 0x8B81 ++#define GL_INFO_LOG_LENGTH 0x8B84 ++#define GL_SHADER_SOURCE_LENGTH 0x8B88 ++#define GL_SHADER_COMPILER 0x8DFA ++#define GL_SHADER_BINARY_FORMATS 0x8DF8 ++#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 ++#define GL_LOW_FLOAT 0x8DF0 ++#define GL_MEDIUM_FLOAT 0x8DF1 ++#define GL_HIGH_FLOAT 0x8DF2 ++#define GL_LOW_INT 0x8DF3 ++#define GL_MEDIUM_INT 0x8DF4 ++#define GL_HIGH_INT 0x8DF5 ++#define GL_FRAMEBUFFER 0x8D40 ++#define GL_RENDERBUFFER 0x8D41 ++#define GL_RGBA4 0x8056 ++#define GL_RGB5_A1 0x8057 ++#define GL_RGB565 0x8D62 ++#define GL_DEPTH_COMPONENT16 0x81A5 ++#define GL_STENCIL_INDEX8 0x8D48 ++#define GL_RENDERBUFFER_WIDTH 0x8D42 ++#define GL_RENDERBUFFER_HEIGHT 0x8D43 ++#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 ++#define GL_RENDERBUFFER_RED_SIZE 0x8D50 ++#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 ++#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 ++#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 ++#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 ++#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 ++#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 ++#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 ++#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 ++#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 ++#define GL_COLOR_ATTACHMENT0 0x8CE0 ++#define GL_DEPTH_ATTACHMENT 0x8D00 ++#define GL_STENCIL_ATTACHMENT 0x8D20 ++#define GL_NONE 0 ++#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 ++#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 ++#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 ++#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 ++#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD ++#define GL_FRAMEBUFFER_BINDING 0x8CA6 ++#define GL_RENDERBUFFER_BINDING 0x8CA7 ++#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 ++#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 ++typedef void (GL_APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); ++typedef void (GL_APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); ++typedef void (GL_APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); ++typedef void (GL_APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); ++typedef void (GL_APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); ++typedef void (GL_APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); ++typedef void (GL_APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture); ++typedef void (GL_APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); ++typedef void (GL_APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); ++typedef void (GL_APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); ++typedef void (GL_APIENTRYP PFNGLBLENDFUNCPROC) (GLenum sfactor, GLenum dfactor); ++typedef void (GL_APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); ++typedef void (GL_APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); ++typedef void (GL_APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); ++typedef GLenum (GL_APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); ++typedef void (GL_APIENTRYP PFNGLCLEARPROC) (GLbitfield mask); ++typedef void (GL_APIENTRYP PFNGLCLEARCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); ++typedef void (GL_APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d); ++typedef void (GL_APIENTRYP PFNGLCLEARSTENCILPROC) (GLint s); ++typedef void (GL_APIENTRYP PFNGLCOLORMASKPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); ++typedef void (GL_APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); ++typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); ++typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); ++typedef void (GL_APIENTRYP PFNGLCOPYTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); ++typedef void (GL_APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); ++typedef GLuint (GL_APIENTRYP PFNGLCREATEPROGRAMPROC) (void); ++typedef GLuint (GL_APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); ++typedef void (GL_APIENTRYP PFNGLCULLFACEPROC) (GLenum mode); ++typedef void (GL_APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); ++typedef void (GL_APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); ++typedef void (GL_APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); ++typedef void (GL_APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); ++typedef void (GL_APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); ++typedef void (GL_APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures); ++typedef void (GL_APIENTRYP PFNGLDEPTHFUNCPROC) (GLenum func); ++typedef void (GL_APIENTRYP PFNGLDEPTHMASKPROC) (GLboolean flag); ++typedef void (GL_APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); ++typedef void (GL_APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); ++typedef void (GL_APIENTRYP PFNGLDISABLEPROC) (GLenum cap); ++typedef void (GL_APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); ++typedef void (GL_APIENTRYP PFNGLDRAWARRAYSPROC) (GLenum mode, GLint first, GLsizei count); ++typedef void (GL_APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices); ++typedef void (GL_APIENTRYP PFNGLENABLEPROC) (GLenum cap); ++typedef void (GL_APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); ++typedef void (GL_APIENTRYP PFNGLFINISHPROC) (void); ++typedef void (GL_APIENTRYP PFNGLFLUSHPROC) (void); ++typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); ++typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); ++typedef void (GL_APIENTRYP PFNGLFRONTFACEPROC) (GLenum mode); ++typedef void (GL_APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); ++typedef void (GL_APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); ++typedef void (GL_APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); ++typedef void (GL_APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); ++typedef void (GL_APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures); ++typedef void (GL_APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); ++typedef void (GL_APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); ++typedef void (GL_APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); ++typedef GLint (GL_APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); ++typedef void (GL_APIENTRYP PFNGLGETBOOLEANVPROC) (GLenum pname, GLboolean *data); ++typedef void (GL_APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); ++typedef GLenum (GL_APIENTRYP PFNGLGETERRORPROC) (void); ++typedef void (GL_APIENTRYP PFNGLGETFLOATVPROC) (GLenum pname, GLfloat *data); ++typedef void (GL_APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); ++typedef void (GL_APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *data); ++typedef void (GL_APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); ++typedef void (GL_APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); ++typedef void (GL_APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); ++typedef void (GL_APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); ++typedef void (GL_APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); ++typedef void (GL_APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); ++typedef void (GL_APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); ++typedef const GLubyte *(GL_APIENTRYP PFNGLGETSTRINGPROC) (GLenum name); ++typedef void (GL_APIENTRYP PFNGLGETTEXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); ++typedef void (GL_APIENTRYP PFNGLGETTEXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); ++typedef void (GL_APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); ++typedef void (GL_APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); ++typedef GLint (GL_APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); ++typedef void (GL_APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); ++typedef void (GL_APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); ++typedef void (GL_APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer); ++typedef void (GL_APIENTRYP PFNGLHINTPROC) (GLenum target, GLenum mode); ++typedef GLboolean (GL_APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); ++typedef GLboolean (GL_APIENTRYP PFNGLISENABLEDPROC) (GLenum cap); ++typedef GLboolean (GL_APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer); ++typedef GLboolean (GL_APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); ++typedef GLboolean (GL_APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer); ++typedef GLboolean (GL_APIENTRYP PFNGLISSHADERPROC) (GLuint shader); ++typedef GLboolean (GL_APIENTRYP PFNGLISTEXTUREPROC) (GLuint texture); ++typedef void (GL_APIENTRYP PFNGLLINEWIDTHPROC) (GLfloat width); ++typedef void (GL_APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); ++typedef void (GL_APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param); ++typedef void (GL_APIENTRYP PFNGLPOLYGONOFFSETPROC) (GLfloat factor, GLfloat units); ++typedef void (GL_APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); ++typedef void (GL_APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); ++typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); ++typedef void (GL_APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert); ++typedef void (GL_APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height); ++typedef void (GL_APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); ++typedef void (GL_APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); ++typedef void (GL_APIENTRYP PFNGLSTENCILFUNCPROC) (GLenum func, GLint ref, GLuint mask); ++typedef void (GL_APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask); ++typedef void (GL_APIENTRYP PFNGLSTENCILMASKPROC) (GLuint mask); ++typedef void (GL_APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); ++typedef void (GL_APIENTRYP PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum zpass); ++typedef void (GL_APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); ++typedef void (GL_APIENTRYP PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); ++typedef void (GL_APIENTRYP PFNGLTEXPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat param); ++typedef void (GL_APIENTRYP PFNGLTEXPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); ++typedef void (GL_APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); ++typedef void (GL_APIENTRYP PFNGLTEXPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); ++typedef void (GL_APIENTRYP PFNGLTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); ++typedef void (GL_APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); ++typedef void (GL_APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); ++typedef void (GL_APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); ++typedef void (GL_APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); ++typedef void (GL_APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); ++typedef void (GL_APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); ++typedef void (GL_APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); ++typedef void (GL_APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); ++typedef void (GL_APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); ++typedef void (GL_APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); ++typedef void (GL_APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); ++typedef void (GL_APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height); ++#if GL_GLES_PROTOTYPES ++GL_APICALL void GL_APIENTRY glActiveTexture (GLenum texture); ++GL_APICALL void GL_APIENTRY glAttachShader (GLuint program, GLuint shader); ++GL_APICALL void GL_APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); ++GL_APICALL void GL_APIENTRY glBindBuffer (GLenum target, GLuint buffer); ++GL_APICALL void GL_APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); ++GL_APICALL void GL_APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); ++GL_APICALL void GL_APIENTRY glBindTexture (GLenum target, GLuint texture); ++GL_APICALL void GL_APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); ++GL_APICALL void GL_APIENTRY glBlendEquation (GLenum mode); ++GL_APICALL void GL_APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); ++GL_APICALL void GL_APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor); ++GL_APICALL void GL_APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); ++GL_APICALL void GL_APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); ++GL_APICALL void GL_APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); ++GL_APICALL GLenum GL_APIENTRY glCheckFramebufferStatus (GLenum target); ++GL_APICALL void GL_APIENTRY glClear (GLbitfield mask); ++GL_APICALL void GL_APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); ++GL_APICALL void GL_APIENTRY glClearDepthf (GLfloat d); ++GL_APICALL void GL_APIENTRY glClearStencil (GLint s); ++GL_APICALL void GL_APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); ++GL_APICALL void GL_APIENTRY glCompileShader (GLuint shader); ++GL_APICALL void GL_APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); ++GL_APICALL void GL_APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); ++GL_APICALL void GL_APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); ++GL_APICALL void GL_APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); ++GL_APICALL GLuint GL_APIENTRY glCreateProgram (void); ++GL_APICALL GLuint GL_APIENTRY glCreateShader (GLenum type); ++GL_APICALL void GL_APIENTRY glCullFace (GLenum mode); ++GL_APICALL void GL_APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); ++GL_APICALL void GL_APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); ++GL_APICALL void GL_APIENTRY glDeleteProgram (GLuint program); ++GL_APICALL void GL_APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); ++GL_APICALL void GL_APIENTRY glDeleteShader (GLuint shader); ++GL_APICALL void GL_APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures); ++GL_APICALL void GL_APIENTRY glDepthFunc (GLenum func); ++GL_APICALL void GL_APIENTRY glDepthMask (GLboolean flag); ++GL_APICALL void GL_APIENTRY glDepthRangef (GLfloat n, GLfloat f); ++GL_APICALL void GL_APIENTRY glDetachShader (GLuint program, GLuint shader); ++GL_APICALL void GL_APIENTRY glDisable (GLenum cap); ++GL_APICALL void GL_APIENTRY glDisableVertexAttribArray (GLuint index); ++GL_APICALL void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count); ++GL_APICALL void GL_APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices); ++GL_APICALL void GL_APIENTRY glEnable (GLenum cap); ++GL_APICALL void GL_APIENTRY glEnableVertexAttribArray (GLuint index); ++GL_APICALL void GL_APIENTRY glFinish (void); ++GL_APICALL void GL_APIENTRY glFlush (void); ++GL_APICALL void GL_APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); ++GL_APICALL void GL_APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); ++GL_APICALL void GL_APIENTRY glFrontFace (GLenum mode); ++GL_APICALL void GL_APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); ++GL_APICALL void GL_APIENTRY glGenerateMipmap (GLenum target); ++GL_APICALL void GL_APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers); ++GL_APICALL void GL_APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); ++GL_APICALL void GL_APIENTRY glGenTextures (GLsizei n, GLuint *textures); ++GL_APICALL void GL_APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); ++GL_APICALL void GL_APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); ++GL_APICALL void GL_APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); ++GL_APICALL GLint GL_APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); ++GL_APICALL void GL_APIENTRY glGetBooleanv (GLenum pname, GLboolean *data); ++GL_APICALL void GL_APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); ++GL_APICALL GLenum GL_APIENTRY glGetError (void); ++GL_APICALL void GL_APIENTRY glGetFloatv (GLenum pname, GLfloat *data); ++GL_APICALL void GL_APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); ++GL_APICALL void GL_APIENTRY glGetIntegerv (GLenum pname, GLint *data); ++GL_APICALL void GL_APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); ++GL_APICALL void GL_APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); ++GL_APICALL void GL_APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); ++GL_APICALL void GL_APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); ++GL_APICALL void GL_APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); ++GL_APICALL void GL_APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); ++GL_APICALL void GL_APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); ++GL_APICALL const GLubyte *GL_APIENTRY glGetString (GLenum name); ++GL_APICALL void GL_APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat *params); ++GL_APICALL void GL_APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint *params); ++GL_APICALL void GL_APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params); ++GL_APICALL void GL_APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params); ++GL_APICALL GLint GL_APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); ++GL_APICALL void GL_APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); ++GL_APICALL void GL_APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); ++GL_APICALL void GL_APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer); ++GL_APICALL void GL_APIENTRY glHint (GLenum target, GLenum mode); ++GL_APICALL GLboolean GL_APIENTRY glIsBuffer (GLuint buffer); ++GL_APICALL GLboolean GL_APIENTRY glIsEnabled (GLenum cap); ++GL_APICALL GLboolean GL_APIENTRY glIsFramebuffer (GLuint framebuffer); ++GL_APICALL GLboolean GL_APIENTRY glIsProgram (GLuint program); ++GL_APICALL GLboolean GL_APIENTRY glIsRenderbuffer (GLuint renderbuffer); ++GL_APICALL GLboolean GL_APIENTRY glIsShader (GLuint shader); ++GL_APICALL GLboolean GL_APIENTRY glIsTexture (GLuint texture); ++GL_APICALL void GL_APIENTRY glLineWidth (GLfloat width); ++GL_APICALL void GL_APIENTRY glLinkProgram (GLuint program); ++GL_APICALL void GL_APIENTRY glPixelStorei (GLenum pname, GLint param); ++GL_APICALL void GL_APIENTRY glPolygonOffset (GLfloat factor, GLfloat units); ++GL_APICALL void GL_APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); ++GL_APICALL void GL_APIENTRY glReleaseShaderCompiler (void); ++GL_APICALL void GL_APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); ++GL_APICALL void GL_APIENTRY glSampleCoverage (GLfloat value, GLboolean invert); ++GL_APICALL void GL_APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); ++GL_APICALL void GL_APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); ++GL_APICALL void GL_APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); ++GL_APICALL void GL_APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask); ++GL_APICALL void GL_APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); ++GL_APICALL void GL_APIENTRY glStencilMask (GLuint mask); ++GL_APICALL void GL_APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); ++GL_APICALL void GL_APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); ++GL_APICALL void GL_APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); ++GL_APICALL void GL_APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); ++GL_APICALL void GL_APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param); ++GL_APICALL void GL_APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params); ++GL_APICALL void GL_APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param); ++GL_APICALL void GL_APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint *params); ++GL_APICALL void GL_APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); ++GL_APICALL void GL_APIENTRY glUniform1f (GLint location, GLfloat v0); ++GL_APICALL void GL_APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value); ++GL_APICALL void GL_APIENTRY glUniform1i (GLint location, GLint v0); ++GL_APICALL void GL_APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value); ++GL_APICALL void GL_APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); ++GL_APICALL void GL_APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value); ++GL_APICALL void GL_APIENTRY glUniform2i (GLint location, GLint v0, GLint v1); ++GL_APICALL void GL_APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value); ++GL_APICALL void GL_APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); ++GL_APICALL void GL_APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value); ++GL_APICALL void GL_APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); ++GL_APICALL void GL_APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value); ++GL_APICALL void GL_APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); ++GL_APICALL void GL_APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value); ++GL_APICALL void GL_APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); ++GL_APICALL void GL_APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value); ++GL_APICALL void GL_APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); ++GL_APICALL void GL_APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); ++GL_APICALL void GL_APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); ++GL_APICALL void GL_APIENTRY glUseProgram (GLuint program); ++GL_APICALL void GL_APIENTRY glValidateProgram (GLuint program); ++GL_APICALL void GL_APIENTRY glVertexAttrib1f (GLuint index, GLfloat x); ++GL_APICALL void GL_APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v); ++GL_APICALL void GL_APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); ++GL_APICALL void GL_APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v); ++GL_APICALL void GL_APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); ++GL_APICALL void GL_APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v); ++GL_APICALL void GL_APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); ++GL_APICALL void GL_APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v); ++GL_APICALL void GL_APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); ++GL_APICALL void GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); ++#endif ++#endif /* GL_ES_VERSION_2_0 */ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/src/gpu/ganesh/gl/egl/gl2platform.h b/src/gpu/ganesh/gl/egl/gl2platform.h +new file mode 100644 +index 0000000000..eb318dc3a3 +--- /dev/null ++++ b/src/gpu/ganesh/gl/egl/gl2platform.h +@@ -0,0 +1,38 @@ ++#ifndef __gl2platform_h_ ++#define __gl2platform_h_ ++ ++/* ++** Copyright (c) 2017 The Khronos Group Inc. ++** ++** 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. ++*/ ++ ++/* Platform-specific types and definitions for OpenGL ES 2.X gl2.h ++ * ++ * Adopters may modify khrplatform.h and this file to suit their platform. ++ * Please contribute modifications back to Khronos as pull requests on the ++ * public github repository: ++ * https://github.com/KhronosGroup/OpenGL-Registry ++ */ ++ ++#include ++ ++#ifndef GL_APICALL ++#define GL_APICALL KHRONOS_APICALL ++#endif ++ ++#ifndef GL_APIENTRY ++#define GL_APIENTRY KHRONOS_APIENTRY ++#endif ++ ++#endif /* __gl2platform_h_ */ +diff --git a/src/ports/SkDebug_ohos.cpp b/src/ports/SkDebug_ohos.cpp +new file mode 100644 +index 0000000000..41093f6518 +--- /dev/null ++++ b/src/ports/SkDebug_ohos.cpp +@@ -0,0 +1,50 @@ ++/* ++ * Copyright 2006 The Android Open Source Project ++ * ++ * Use of this source code is governed by a BSD-style license that can be ++ * found in the LICENSE file. ++ */ ++ ++#include "include/private/base/SkDebug.h" ++#include "include/private/base/SkFeatures.h" ++ ++#if defined(SK_BUILD_FOR_OHOS) ++ ++#include ++ ++#ifdef LOG_TAG ++ #undef LOG_TAG ++#endif ++#define LOG_TAG "skia" ++#include ++ ++// Print debug output to stdout as well. This is useful for command line ++// applications (e.g. skia_launcher). ++bool gSkDebugToStdOut = false; ++ ++void SkDebugf(const char format[], ...) { ++ va_list args1, args2; ++ va_start(args1, format); ++ ++ if (gSkDebugToStdOut) { ++ va_copy(args2, args1); ++ vprintf(format, args2); ++ va_end(args2); ++ fflush(stdout); ++ } ++ ++ ++ char buffer[1024]; ++ int len = 0; ++ len = vsnprintf(buffer, sizeof(buffer) - 1, format, args1); ++ if (len <= 0) { ++ return; ++ } ++ buffer[len] = '\0'; ++ OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_DOMAIN, LOG_TAG, ++ " %{public}s",buffer); ++ ++ va_end(args1); ++} ++ ++#endif // defined(SK_BUILD_FOR_OHOS) +diff --git a/src/ports/SkDebug_stdio.cpp b/src/ports/SkDebug_stdio.cpp +index 6a58936900..004102aead 100644 +--- a/src/ports/SkDebug_stdio.cpp ++++ b/src/ports/SkDebug_stdio.cpp +@@ -9,7 +9,7 @@ + #include "include/private/base/SkFeatures.h" + #include "include/private/base/SkLoadUserConfig.h" + +-#if !defined(SK_BUILD_FOR_WIN) && !defined(SK_BUILD_FOR_ANDROID) ++#if !defined(SK_BUILD_FOR_WIN) && !defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_OHOS) + + #include + #include +diff --git a/src/ports/skia_ohos/FontConfig_ohos.cpp b/src/ports/skia_ohos/FontConfig_ohos.cpp +new file mode 100644 +index 0000000000..483083bf28 +--- /dev/null ++++ b/src/ports/skia_ohos/FontConfig_ohos.cpp +@@ -0,0 +1,1336 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "FontConfig_ohos.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "SkFontStyle.h" ++#include "SkString.h" ++#include "SkTypeface_ohos.h" ++#include "include/private/base/SkDebug.h" ++ ++using namespace ErrorCode; ++#if defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_WIN) or defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_MAC) or defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_LINUX) ++static const char* OHOS_DEFAULT_CONFIG = "fontconfig.json"; ++#else ++static const char* OHOS_DEFAULT_CONFIG = "/system/etc/fontconfig.json"; ++#endif ++ ++static const char* PRODUCT_DEFAULT_CONFIG = "/system/etc/productfontconfig.json"; ++/*! Constructor ++ * \param fontScanner the scanner to get the font information from a font file ++ * \param fname the full name of system font configuration document. ++ * \n The default value is '/system/etc/fontconfig.json', if fname is given null ++ */ ++FontConfig_OHOS::FontConfig_OHOS(const SkFontScanner& fontScanner, ++ const char* fname) ++{ ++ int err = checkProductFile(fname); ++ if (err != NO_ERROR) { ++ return; ++ } ++ scanFonts(fontScanner); ++ resetGenericValue(); ++ resetFallbackValue(); ++} ++ ++/*! To get the fallbackForMap ++ * \return The reference of fallbackForMap ++ */ ++const FallbackForMap& FontConfig_OHOS::getFallbackForMap() const ++{ ++ return fallbackForMap; ++} ++ ++/*! To get the fallback set ++ * \return The reference of fallbackSet ++ */ ++const FallbackSet& FontConfig_OHOS::getFallbackSet() const ++{ ++ return fallbackSet; ++} ++ ++/*! To get the count of font style sets supported in the system ++ * \return The count of font style sets in generic family ++ */ ++int FontConfig_OHOS::getFamilyCount() const ++{ ++ return genericFamilySet.size(); ++} ++ ++/*! To get the family name of the default font style set ++ * \param[out] familyName a pointer of SkString object, to which the family value will be set. ++ * \return The count of typeface in this font style set ++ * \n Return -1, if there is no any font style set in the system. ++ */ ++int FontConfig_OHOS::getDefaultFamily(SkString* familyName) const ++{ ++ return getFamilyName(0, familyName); ++} ++ ++/*! To get the family name of a font style set ++ * \param index the index of a font style set in generic family ++ * \param[out] familyName a pointer of SkString object, to which the family value will be set ++ * \return The count of typeface in the font style set ++ * \n Return -1, if the 'index' is out of range ++ */ ++int FontConfig_OHOS::getFamilyName(int index, SkString* familyName) const ++{ ++ if (index < 0 || index >= this->getFamilyCount()) { ++ if (familyName) { ++ familyName->reset(); ++ } ++ return -1; ++ } ++ if (familyName) { ++ *familyName = genericFamilySet[index]->familyName; ++ } ++ return genericFamilySet[index]->typefaceSet->size(); ++} ++ ++/*! To get the count of a font style set ++ * \param styleIndex the index of a font style set ++ * \param isFallback to indicate the font style set is from generic family or fallback family ++ * \n false , the font style set is from generic family list ++ * \n true, the font style set is from fallback family list ++ * \return The count of typeface in the font style set ++ */ ++int FontConfig_OHOS::getTypefaceCount(int styleIndex, bool isFallback) const ++{ ++ if (styleIndex < 0) { ++ return -1; ++ } ++ if (isFallback) { ++ if ((unsigned int)styleIndex < fallbackSet.size()) { ++ return fallbackSet[styleIndex]->typefaceSet->size(); ++ } ++ } else { ++ if ((unsigned int)styleIndex < genericFamilySet.size()) { ++ return genericFamilySet[styleIndex]->typefaceSet->size(); ++ } ++ } ++ return -1; ++} ++ ++/*! To get a typeface ++ * \param styleIndex the index of a font style set ++ * \param index the index of a typeface in its style set ++ * \param isFallback false, the font style set is generic ++ * \n true, the font style set is fallback ++ * \return The pointer of a typeface ++ * \n Return null, if 'styleIndex' or 'index' is out of range ++ */ ++sk_sp FontConfig_OHOS::getTypeface(int styleIndex, int index, ++ bool isFallback) const ++{ ++ if (styleIndex < 0 || index < 0 || ++ (isFallback && (unsigned int)styleIndex >= fallbackSet.size()) || ++ (!isFallback && (unsigned int)styleIndex >= genericFamilySet.size())) { ++ return nullptr; ++ } ++ if (isFallback) { ++ const TypefaceSet& tpSet = *(fallbackSet[styleIndex]->typefaceSet.get()); ++ if ((unsigned int)index < tpSet.size()) { ++ return tpSet[index]; ++ } ++ } else { ++ const TypefaceSet& tpSet = *(genericFamilySet[styleIndex]->typefaceSet.get()); ++ if ((unsigned int)index < tpSet.size()) { ++ return tpSet[index]; ++ } ++ } ++ return nullptr; ++} ++ ++/*! To get a typeface ++ * \param styleIndex the index a font style set ++ * \param style the font style to be matching ++ * \param isFallback false, the font style set is generic ++ * \n true, the font style set is fallback ++ * \return An object of typeface whose font style is the closest matching to 'style' ++ * \n Return null, if 'styleIndex' is out of range ++ */ ++sk_sp FontConfig_OHOS::getTypeface(int styleIndex, const SkFontStyle& style, ++ bool isFallback) const ++{ ++ if (styleIndex < 0 || ++ (isFallback && (unsigned int)styleIndex >= fallbackSet.size()) || ++ (!isFallback && (unsigned int)styleIndex >= genericFamilySet.size())) { ++ return nullptr; ++ } ++ const TypefaceSet* pSet = nullptr; ++ if (isFallback) { ++ pSet = fallbackSet[styleIndex]->typefaceSet.get(); ++ } else { ++ pSet = genericFamilySet[styleIndex]->typefaceSet.get(); ++ } ++ sk_sp tp = matchFontStyle(*pSet, style); ++ return tp; ++} ++ ++/*! To get the index of a font style set ++ * \param familyName the family name of the font style set ++ * \n get the index of default font style set, if 'familyName' is null ++ * \param[out] isFallback to tell if the family is from generic or fallback to the caller. ++ * \n isFallback is false, if the font style is from generic family list ++ * \n isFallback is true, if the font style is from fallback family list ++ * \return The index of the font style set ++ * \n Return -1, if 'familyName' is not found in the system ++ */ ++int FontConfig_OHOS::getStyleIndex(const char* familyName, bool& isFallback) const ++{ ++ if (familyName == nullptr) { ++ isFallback = false; ++ return 0; ++ } ++ SkString fname(familyName); ++ int* p = genericNames.find(fname); ++ if (p) { ++ isFallback = false; ++ return *p; ++ } else { ++ p = fallbackNames.find(fname); ++ if (p) { ++ isFallback = true; ++ return *p; ++ } ++ } ++ return -1; ++} ++ ++/*! Find the closest matching typeface ++ * \param typefaceSet a typeface set belonging to the same font style set ++ * \param pattern the font style to be matching ++ * \return The typeface object which is the closest matching to 'pattern' ++ * \n Return null, if the count of typeface is 0 ++ */ ++sk_sp FontConfig_OHOS::matchFontStyle(const TypefaceSet& typefaceSet, ++ const SkFontStyle& pattern) ++{ ++ int count = typefaceSet.size(); ++ if (count == 1) { ++ return typefaceSet[0]; ++ } ++ sk_sp res = nullptr; ++ uint32_t minDiff = 0xFFFFFFFF; ++ for (int i = 0; i < count; i++) { ++ const SkFontStyle& fontStyle = typefaceSet[i]->fontStyle(); ++ uint32_t diff = getFontStyleDifference(pattern, fontStyle); ++ if (diff < minDiff) { ++ minDiff = diff; ++ res = typefaceSet[i]; ++ } ++ } ++ return res; ++} ++ ++/*! To get the difference between a font style and the matching font style ++ * \param dstStyle the style to be matching ++ * \param srcStyle a font style ++ * \return The difference value of a specified style with the matching style ++ */ ++uint32_t FontConfig_OHOS::getFontStyleDifference(const SkFontStyle& dstStyle, ++ const SkFontStyle& srcStyle) ++{ ++ int normalWidth = SkFontStyle::kNormal_Width; ++ int dstWidth = dstStyle.width(); ++ int srcWidth = srcStyle.width(); ++ ++ uint32_t widthDiff = 0; ++ // The maximum font width is kUltraExpanded_Width i.e. '9'. ++ // If dstWidth <= kNormal_Width (5), first check narrower values, then wider values. ++ // If dstWidth > kNormal_Width, first check wider values, then narrower values. ++ // When dstWidth and srcWidth are at different side of kNormal_Width, ++ // the width difference between them should be more than 5 (9/2+1) ++ if (dstWidth <= normalWidth) { ++ if (srcWidth <= dstWidth) { ++ widthDiff = dstWidth - srcWidth; ++ } else { ++ widthDiff = srcWidth - dstWidth + 5; ++ } ++ } else { ++ if (srcWidth >= dstWidth) { ++ widthDiff = srcWidth - dstWidth; ++ } else { ++ widthDiff = dstWidth - srcWidth + 5; ++ } ++ } ++ ++ int diffSlantValue[3][3] = { ++ {0, 2, 1}, ++ {2, 0, 1}, ++ {2, 1, 0} ++ }; ++ uint32_t slantDiff = diffSlantValue[dstStyle.slant()][srcStyle.slant()]; ++ ++ int dstWeight = dstStyle.weight(); ++ int srcWeight = srcStyle.weight(); ++ uint32_t weightDiff = 0; ++ ++ // The maximum weight is kExtraBlack_Weight (1000), when dstWeight and srcWeight are at the different ++ // side of kNormal_Weight, the weight difference between them should be more than 500 (1000/2) ++ if ((dstWeight == SkFontStyle::kNormal_Weight && srcWeight == SkFontStyle::kMedium_Weight) || ++ (dstWeight == SkFontStyle::kMedium_Weight && srcWeight == SkFontStyle::kNormal_Weight)) { ++ weightDiff = 50; ++ } else if (dstWeight <= SkFontStyle::kNormal_Weight) { ++ if (srcWeight <= dstWeight) { ++ weightDiff = dstWeight - srcWeight; ++ } else { ++ weightDiff = srcWeight - dstWeight + 500; ++ } ++ } else if (dstWeight > SkFontStyle::kNormal_Weight) { ++ if (srcWeight >= dstWeight) { ++ weightDiff = srcWeight - dstWeight; ++ } else { ++ weightDiff = dstWeight - srcWeight + 500; ++ } ++ } ++ // The first 2 bytes to save weight difference, the third byte to save slant difference, ++ // and the fourth byte to save width difference ++ uint32_t diff = (widthDiff << 24) + (slantDiff << 16) + weightDiff; ++ return diff; ++} ++ ++/*! To get the data of font configuration file ++ * \param fname the full name of the font configuration file ++ * \param[out] size the size of data returned to the caller ++ * \return The pointer of content of the file ++ * \note The returned pointer should be freed by the caller ++ */ ++char* FontConfig_OHOS::getFileData(const char* fname, int& size) ++{ ++ FILE* fp = fopen(fname, "r"); ++ if (fp == nullptr) { ++ return nullptr; ++ } ++ fseek(fp, 0L, SEEK_END); ++ size = ftell(fp) + 1; ++ rewind(fp); ++ void* data = malloc(size); ++ if (data == nullptr) { ++ fclose(fp); ++ return nullptr; ++ } ++ memset(data, 0, size); ++ (void) fread(data, size, 1, fp); ++ fclose(fp); ++ return (char*)data; ++} ++ ++/*! parse the system font configuration document ++ * \param fname the full name of the font configuration document ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_NOT_FOUND config document is not found ++ * \return ERROR_CONFIG_FORMAT_NOT_SUPPORTED config document format is not supported ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE wrong type of value in the configuration ++ */ ++int FontConfig_OHOS::parseConfig(const char* fname) ++{ ++ if (fname == nullptr) { ++ fname = OHOS_DEFAULT_CONFIG; ++ } ++ Json::Value root; ++ int err = checkConfigFile(fname, root); ++ if (err != NO_ERROR) { ++ return err; ++ } ++ // "fontdir" - optional, the data type should be string ++ const char* key = "fontdir"; ++ if (root.isMember(key)) { ++ if (root[key].isArray()) { ++ parseFontDir(root[key]); ++ } else { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key); ++ } ++ } ++ // "generic", "fallback" - necessary, the data type should be array ++ const char* keys[] = {"generic", "fallback", nullptr}; ++ int index = 0; ++ while (true) { ++ if (keys[index] == nullptr) { ++ break; ++ } ++ key = keys[index++]; ++ if (!root.isMember(key)) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, key); ++ } else if (!root[key].isArray()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::arrayValue, root[key].type()); ++ } ++ const Json::Value& arr = root[key]; ++ for (unsigned int i = 0; i < arr.size(); i++) { ++ if (arr[i].isObject()) { ++ if (!strcmp(key, "generic")) { ++ parseGeneric(arr[i]); ++ } else if (!strcmp(key, "fallback")) { ++ parseFallback(arr[i]); ++ } ++ } else { ++ SkString errKey; ++ errKey.appendf("%s#%d", key, i + 1); ++ (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, errKey.c_str(), ++ Json::objectValue, arr[i].type()); ++ } ++ } ++ } ++ root.clear(); ++ return NO_ERROR; ++} ++ ++/*! check the system font configuration document ++ * \param fname the full name of the font configuration document ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_NOT_FOUND config document is not found ++ * \return ERROR_CONFIG_FORMAT_NOT_SUPPORTED config document format is not supported ++ */ ++int FontConfig_OHOS::checkConfigFile(const char* fname, Json::Value& root) ++{ ++ int size = 0; ++ char* data = getFileData(fname, size); ++ if (data == nullptr) { ++ return logErrInfo(ERROR_CONFIG_NOT_FOUND, fname); ++ } ++ std::string errs; ++ Json::CharReaderBuilder charReaderBuilder; ++ std::unique_ptr jsonReader(charReaderBuilder.newCharReader()); ++ bool isJson = jsonReader->parse(data, data + size, &root, &errs); ++ free((void*)data); ++ data = nullptr; ++ ++ if (!isJson || !errs.empty()) { ++ return logErrInfo(ERROR_CONFIG_FORMAT_NOT_SUPPORTED, fname); ++ } ++ return NO_ERROR; ++} ++#if ENABLE_DEBUG ++/*! To print out the font information ++ * \param font the font object to be printed ++ */ ++void FontConfig_OHOS::dumpFont(const FontInfo& font) const ++{ ++ SkDEBUGF("name=%s, family=%s, weight=%d, width=%d, slant=%d, index=%d", ++ font.fname.c_str(), font.familyName.c_str(), font.style.weight(), font.style.width(), font.style.slant(), ++ font.index); ++ int count = font.axisSet.axis.size(); ++ if (count > 0) { ++ SkString str; ++ for (unsigned int i = 0; i < count; i++) { ++ str.appendU32(SkFixedFloorToInt(font.axisSet.axis[i])); ++ if (i < count - 1) { ++ str.append(","); ++ } ++ } ++ SkDEBUGF("axis={%s}\n", str.c_str()); ++ } ++} ++ ++/*! To print out the information of generic font style set ++ */ ++void FontConfig_OHOS::dumpGeneric() const ++{ ++ SkDEBUGF("\n"); ++ for (unsigned int i = 0; i < genericFamilySet.size(); i++) { ++ SkDEBUGF("[%d] familyName : %s - %d\n", i, genericFamilySet[i]->familyName.c_str(), ++ static_cast(genericFamilySet[i]->typefaceSet->size())); ++ for (int j = 0; j < genericFamilySet[i]->typefaceSet->size(); j++) { ++ if ((*(genericFamilySet[i]->typefaceSet))[j].get()) { ++ const FontInfo* font = (*(genericFamilySet[i]->typefaceSet))[j]->getFontInfo(); ++ if (font) { ++ dumpFont(*font); ++ } else { ++ SkDEBUGF("font [%d] is null\n", j); ++ } ++ } else { ++ SkDEBUGF("typefeace [%d] is null\n", j); ++ } ++ } ++ } ++} ++ ++/*! To print out the information of fallback font style set ++ */ ++void FontConfig_OHOS::dumpFallback() const ++{ ++ SkDEBUGF("\n"); ++ int count = 0; ++ fallbackForMap.foreach([this, &count](const SkString& key, ++ const FallbackSetPos& setIndex) { ++ SkDEBUGF("[%d] family : %s - %d\n", count++, key.c_str(), setIndex.count); ++ for (unsigned int i = setIndex.index; i < setIndex.index + setIndex.count; i++) { ++ const TypefaceSet& tpSet = *(fallbackSet[i]->typefaceSet.get()); ++ SkDEBUGF("[%s] - %d\n", fallbackSet[i]->familyName.c_str(), static_cast(tpSet.size())); ++ ++ for (unsigned int j = 0; j < tpSet.size(); j++) { ++ const FontInfo* font = tpSet[j]->getFontInfo(); ++ if (font) { ++ this->dumpFont(*font); ++ } else { ++ SkDEBUGF("font [%d] is null\n", j); ++ } ++ } ++ } ++ }); ++} ++#endif ++ ++/*! To parse 'fontdir' attribute ++ * \param root the root node of 'fontdir' ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type ++ */ ++int FontConfig_OHOS::parseFontDir(const Json::Value& root) ++{ ++ for (unsigned int i = 0; i < root.size(); i++) { ++ if (root[i].isString()) { ++ const char* dir = root[i].asCString(); ++ fontDirSet.emplace_back(SkString(dir)); ++ } else { ++ SkString text; ++ text.appendf("fontdir#%d", i + 1); ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, text.c_str(), Json::stringValue, root[i].type()); ++ } ++ } ++ return NO_ERROR; ++} ++ ++/*! To parse an item of 'generic' family ++ * \param root the root node of an item in 'generic' list ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute ++ * \return ERROR_CONFIG_MISSING_TAG missing tag of 'family' or 'alias' ++ */ ++int FontConfig_OHOS::parseGeneric(const Json::Value& root) ++{ ++ // "family" - necessary, the data type should be String ++ const char* key = "family"; ++ if (!root.isMember(key)) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, key); ++ } else if (!root[key].isString()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::stringValue, root[key].type()); ++ } ++ SkString familyName = SkString(root[key].asCString()); ++ // "alias" - necessary, the data type should be Array ++ if (!root.isMember("alias")) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, "alias"); ++ } ++ // "adjust", "variation" - optional ++ const char* tags[] = {"alias", "adjust", "variations", "index"}; ++ std::vector aliasSet; ++ std::vector adjustSet; ++ std::vector variationSet; ++ for (unsigned int i = 0; i < sizeof(tags) / sizeof(char*); i++) { ++ key = tags[i]; ++ if (!root.isMember(key)) { ++ continue; ++ } ++ if (root[key].isArray()) { ++ if (!strcmp(key, "index")) { ++ parseTtcIndex(root[key], familyName); ++ continue; ++ } ++ const Json::Value& arr = root[key]; ++ for (unsigned int j = 0; j < arr.size(); j++) { ++ if (arr[j].isObject()) { ++ if (!strcmp(key, "alias")) { ++ parseAlias(arr[j], aliasSet); ++ } else if (!strcmp(key, "adjust")) { ++ parseAdjust(arr[j], adjustSet); ++ } else { ++ parseVariation(arr[j], variationSet); ++ } ++ } else { ++ SkString text; ++ text.appendf("%s#%d", key, j + 1); ++ (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, text.c_str(), Json::objectValue, ++ arr[j].type()); ++ } ++ } ++ } else { ++ (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::arrayValue, root[key].type()); ++ } ++ if (root.size() == 2) { ++ break; ++ } ++ } ++ if (aliasSet.size()) { ++ aliasMap.set(SkString(familyName), aliasSet); ++ } ++ if (adjustSet.size()) { ++ adjustMap.set(SkString(familyName), adjustSet); ++ } ++ if (variationSet.size()) { ++ variationMap.set(SkString(familyName), variationSet); ++ } ++ return NO_ERROR; ++} ++ ++/*! To parse an item of 'alias' attribute ++ * \param root the root node of an item in an 'alias' list ++ * \param[out] aliasSet the value of AliasInfo will be written to and returned to the caller ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute ++ * \return ERROR_CONFIG_MISSING_TAG missing tag of alias name ++ */ ++int FontConfig_OHOS::parseAlias(const Json::Value& root, std::vector& aliasSet) ++{ ++ if (root.empty()) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, "generic-alias-name"); ++ } ++ Json::Value::Members members = root.getMemberNames(); ++ const char* key = members[0].c_str(); ++ if (!root[key].isInt()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, "generic-alias-weight", ++ Json::intValue, root[key].type()); ++ } ++ ++ SkString aliasName = SkString(key); ++ int weight = root[key].asInt(); ++ std::unique_ptr genericFamily = std::make_unique(); ++ genericFamily->familyName = SkString(key); ++ if (aliasSet.size() == 0 || weight > 0) { ++ genericFamily->typefaceSet = std::make_shared(); ++ } else { ++ int index = aliasSet[0].pos; ++ genericFamily->typefaceSet = genericFamilySet[index]->typefaceSet; ++ } ++ genericNames.set(SkString(genericFamily->familyName), genericFamilySet.size()); ++ ++ AliasInfo info = {static_cast(genericFamilySet.size()), weight}; ++ aliasSet.emplace_back(std::move(info)); ++ genericFamilySet.emplace_back(std::move(genericFamily)); ++ return NO_ERROR; ++} ++ ++/*! To parse an item of 'adjust' attribute ++ * \param root the root node of an item in an 'adjust' list ++ * \param[out] adjustSet the value of AdjustInfo will be written to and returned to the caller ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute ++ * \return ERROR_CONFIG_MISSING_TAG missing tag of 'weight' or 'to' ++ */ ++int FontConfig_OHOS::parseAdjust(const Json::Value& root, std::vector& adjustSet) ++{ ++ const char* tags[] = {"weight", "to"}; ++ int values[2]; // value[0] - to save 'weight', value[1] - to save 'to' ++ for (unsigned int i = 0; i < sizeof(tags) / sizeof(char*); i++) { ++ const char* key = tags[i]; ++ if (!root.isMember(key)) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, key); ++ } else if (!root[key].isInt()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, ++ Json::intValue, root[key].type()); ++ } else { ++ values[i] = root[key].asInt(); ++ } ++ } ++ AdjustInfo info = {values[0], values[1]}; ++ adjustSet.push_back(info); ++ return NO_ERROR; ++} ++ ++/*! To parse an item of 'fallback' attribute ++ * \param root the root node of an item in 'fallback' list ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute ++ * \return ERROR_CONFIG_MISSING_TAG missing tag of fallbackFor ++ */ ++int FontConfig_OHOS::parseFallback(const Json::Value& root) ++{ ++ if (root.empty()) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, "fallback-fallbackFor"); ++ } ++ Json::Value::Members members = root.getMemberNames(); ++ const char* key = members[0].c_str(); ++ if (!root[key].isArray()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, "fallback-items", ++ Json::arrayValue, root[key].type()); ++ } ++ unsigned int startPos = fallbackSet.size(); ++ SkString fallbackFor = SkString(key); ++ const Json::Value& fallbackArr = root[key]; ++ for (unsigned int i = 0; i < fallbackArr.size(); i++) { ++ if (!fallbackArr[i].isObject()) { ++ SkString text; ++ text.appendf("fallback-%s#%d", key, i + 1); ++ (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, text.c_str(), Json::objectValue, ++ fallbackArr[i].type()); ++ continue; ++ } ++ parseFallbackItem(fallbackArr[i]); ++ } ++ FallbackSetPos setPos = {startPos, (unsigned int)(fallbackSet.size() - startPos)}; ++ fallbackForMap.set(fallbackFor, setPos); ++ return NO_ERROR; ++} ++ ++/*! To parse an item of fallback family ++ * \param root the root node of a fallback item ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute ++ * \return ERROR_CONFIG_MISSING_TAG missing tag of language ++ */ ++int FontConfig_OHOS::parseFallbackItem(const Json::Value& root) ++{ ++ if (root.empty()) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, "fallback-item-lang"); ++ } ++ Json::Value::Members members = root.getMemberNames(); ++ const char* key = nullptr; ++ bool hasIndex = false; ++ bool hasVariations = false; ++ for (unsigned int i = 0; i < members.size(); i++) { ++ if (members[i] == "variations") { ++ hasVariations = true; ++ } else if (members[i] == "index") { ++ hasIndex = true; ++ } else { ++ key = members[i].c_str(); ++ } ++ } ++ if (key == nullptr) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, "fallback-item-lang"); ++ } ++ if (!root[key].isString()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, "fallback-item-family", ++ Json::stringValue, root[key].type()); ++ } ++ SkString lang = SkString(key); ++ SkString familyName = SkString(root[key].asCString()); ++ if (hasVariations) { ++ key = "variations"; ++ if (root[key].isArray()) { ++ const Json::Value& varArr = root[key]; ++ std::vector variationSet; ++ for (unsigned int i = 0; i < varArr.size(); i++) { ++ if (varArr[i].isObject()) { ++ parseVariation(varArr[i], variationSet); ++ } else { ++ SkString text = SkString("variations#"); ++ text.appendU32(i + 1); ++ (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, text.c_str(), ++ Json::objectValue, varArr[i].type()); ++ } ++ } ++ if (variationSet.size()) { ++ variationMap.set(SkString(familyName), variationSet); ++ } ++ } else { ++ (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::arrayValue, ++ root[key].type()); ++ } ++ } ++ if (hasIndex) { ++ key = "index"; ++ if (root[key].isArray()) { ++ parseTtcIndex(root[key], familyName); ++ } else { ++ (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::arrayValue, root[key].type()); ++ } ++ } ++ std::unique_ptr fallback = std::make_unique(); ++ fallback->familyName = familyName; ++ fallback->langs = lang; ++ fallback->typefaceSet = std::make_shared(); ++ fallbackNames.set(SkString(familyName), fallbackSet.size()); ++ fallbackSet.emplace_back(std::move(fallback)); ++ return NO_ERROR; ++} ++ ++/*! To parse an item of 'variations' attribute ++ * \param root the root node of an item in 'variations' list ++ * \param[out] variationSet the value of VariationInfo is written to and returned to the caller ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute ++ * \return ERROR_CONFIG_MISSING_TAG missing tag of 'weight' or 'wght' ++ */ ++int FontConfig_OHOS::parseVariation(const Json::Value& root, std::vector& variationSet) ++{ ++ const char* key = nullptr; ++ const char* tags[] = {"wght", "wdth", "slnt", "weight", "width", "slant"}; ++ VariationInfo info; ++ for (unsigned int i = 0; i < sizeof(tags) / sizeof(char*); i++) { ++ key = tags[i]; ++ if ((!strcmp(key, "wght") || !strcmp(key, "weight")) && ++ !root.isMember(key)) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, key); ++ } ++ if (!root.isMember(key)) { ++ continue; ++ } ++ if (!strcmp(key, "weight")) { ++ if (!root[key].isInt()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::intValue, root[key].type()); ++ } ++ info.weight = root[key].asInt(); ++ } else if (!strcmp(key, "width")) { ++ if (!root[key].isInt()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::intValue, root[key].type()); ++ } ++ info.width = root[key].asInt(); ++ } else if (!strcmp(key, "slant")) { ++ if (!root[key].isString()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::stringValue, root[key].type()); ++ } ++ const char* str = root[key].asCString(); ++ if (!strcmp(str, "normal")) { ++ info.slant = static_cast(SkFontStyle::kUpright_Slant); ++ } else if (!strcmp(str, "italic")) { ++ info.slant = static_cast(SkFontStyle::kItalic_Slant); ++ } else if (!strcmp(str, "oblique")) { ++ info.slant = static_cast(SkFontStyle::kOblique_Slant); ++ } ++ } else { ++ if (!root[key].isNumeric()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::realValue, root[key].type()); ++ } ++ Coordinate axis; ++ axis.axis = SkSetFourByteTag(key[0], key[1], key[2], key[3]); ++ axis.value = root[key].asFloat(); ++ info.axis.emplace_back(axis); ++ } ++ } ++ variationSet.emplace_back(info); ++ return NO_ERROR; ++} ++ ++/*! To parse 'index' attribute ++ * \param root the root node of 'index' attribute ++ * \param familyName the name of the family which the root node belongs to ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute ++ */ ++int FontConfig_OHOS::parseTtcIndex(const Json::Value& root, const SkString& familyName) ++{ ++ unsigned int keyCount = 2; // the value of 'index' is an array with 2 items. ++ if (root.size() == keyCount && root[0].isString() && root[1].isNumeric()) { ++ TtcIndexInfo item = { SkString(root[0].asCString()), root[1].asInt() }; ++ if (item.ttcIndex != 0 && ttcIndexMap.find(item.familyName) == nullptr) { ++ ttcIndexMap.set(SkString(item.familyName), {SkString(item.familyName), 0}); ++ } ++ ttcIndexMap.set(SkString(familyName), item); ++ } else { ++ int ret = ERROR_CONFIG_INVALID_VALUE_TYPE; ++ SkString text; ++ const char* key = "index"; ++ if (root.size() != keyCount) { ++ text.appendf("%s#0", key); ++ errSet.emplace_back(ret, text.c_str()); ++ SkDEBUGF("%s : '%s' size should be 2, but here it's %d\n", errToString(ret), key, root.size()); ++ return ret; ++ } else if (!root[0].isString()) { ++ text.appendf("%s#1", key); ++ return logErrInfo(ret, text.c_str(), Json::stringValue, root[0].type()); ++ } else { ++ text.appendf("%s#2", key); ++ return logErrInfo(ret, text.c_str(), Json::intValue, root[1].type()); ++ } ++ } ++ return NO_ERROR; ++} ++ ++/*! To get the axis value and set to 'font' ++ * \param axisDefs the axis ranges of a font ++ * \param variation the variation data from which axis values are generated ++ * \param[out] font the axis values will be written to and returned to the caller ++ */ ++void FontConfig_OHOS::getAxisValues(const AxisDefinitions& axisDefs, ++ const VariationInfo& variation, FontInfo& font) const ++{ ++ SkFontArguments::VariationPosition position; ++ position.coordinateCount = variation.axis.size(); ++ position.coordinates = variation.axis.data(); ++ ++ int count = axisDefs.size(); ++ SkFixed axisValues[count]; ++ SkFontScanner_FreeType::computeAxisValues(axisDefs, position, ++ axisValues, font.familyName); ++ font.axisSet.axis.clear(); ++ font.axisSet.range.clear(); ++ for (int i = 0; i < count; i++) { ++ font.axisSet.axis.emplace_back(axisValues[i]); ++ font.axisSet.range.emplace_back(axisDefs[i]); ++ } ++} ++ ++/*! To insert a ttc font into a font style set ++ * \param count the count of typeface in a ttc font ++ * \param font an object of the FontInfo with font information ++ * \return true, if the font is a ttc font and added to corresponding font style set ++ * \return false, if the font is not a ttc font ++ */ ++bool FontConfig_OHOS::insertTtcFont(int count, FontInfo& font) ++{ ++ bool ret = false; ++ ttcIndexMap.foreach([this, count, &font, &ret] ++ (const SkString& familyName, TtcIndexInfo* info) { ++ if (info->familyName == font.familyName && info->ttcIndex < count) { ++ SkString specifiedName; ++ TypefaceSet* tpSet = this->getTypefaceSet(familyName, specifiedName); ++ if (tpSet) { ++ FontInfo newFont(font); ++ newFont.familyName = familyName; ++ newFont.index = info->ttcIndex; ++ sk_sp typeface = sk_make_sp(specifiedName, newFont); ++ tpSet->push_back(std::move(typeface)); ++ ret = true; ++ } ++ } ++ }); ++ return ret; ++} ++ ++/*! To insert a variable font into a font style set ++ * \param axisDefs the axis ranges of a variable font ++ * \param font an object of the FontInfo with font information ++ * \return true, if the font is a variable and some typefaces are added to the corresponding font style set ++ * \return false, if the font is not variable ++ */ ++bool FontConfig_OHOS::insertVariableFont(const AxisDefinitions& axisDefs, FontInfo& font) ++{ ++ const SkString& key = font.familyName; ++ if (variationMap.find(key) == nullptr || axisDefs.size() == 0) { ++ return false; ++ } ++ SkString specifiedName; ++ TypefaceSet* tpSet = getTypefaceSet(key, specifiedName); ++ if (tpSet == nullptr) { ++ return false; ++ } ++ const std::vector& variationSet = *(variationMap.find(key)); ++ for (unsigned int i = 0; i < variationSet.size(); i++) { ++ FontInfo newFont(font); ++ getAxisValues(axisDefs, variationSet[i], newFont); ++ int width = font.style.width(); ++ SkFontStyle::Slant slant = font.style.slant(); ++ if (variationSet[i].width != -1) { ++ width = variationSet[i].width; ++ } ++ if (variationSet[i].slant != -1) { ++ slant = (SkFontStyle::Slant) variationSet[i].slant; ++ } ++ newFont.style = SkFontStyle(variationSet[i].weight, width, slant); ++ sk_sp typeface = sk_make_sp(specifiedName, newFont); ++ tpSet->push_back(std::move(typeface)); ++ } ++ return true; ++} ++ ++/*! To get the typeface set of a font style set ++ * \param familyName the family name of a font style set ++ * \param[out] specifiedName the specified family name of a font style set returned to the caller ++ * \return The object of typeface set ++ * \n Return null, if the family name is not found in the system ++ */ ++TypefaceSet* FontConfig_OHOS::getTypefaceSet(const SkString& familyName, ++ SkString& specifiedName) const ++{ ++ if (aliasMap.find(familyName) != nullptr) { ++ const std::vector& aliasSet = *(aliasMap.find(familyName)); ++ if (aliasSet.size()) { ++ int index = aliasSet[0].pos; ++ specifiedName = genericFamilySet[index]->familyName; ++ return genericFamilySet[index]->typefaceSet.get(); ++ } ++ } else if (fallbackNames.find(familyName) != nullptr) { ++ int index = *(fallbackNames.find(familyName)); ++ return fallbackSet[index]->typefaceSet.get(); ++ } ++ return nullptr; ++} ++ ++/*! To load font information from a font file ++ * \param scanner a scanner used to parse the font file ++ * \param fname the full name of a font file ++ * \return NO_ERROR successful ++ * \return ERROR_FONT_NOT_EXIST font file is not exist ++ * \return ERROR_FONT_INVALID_STREAM the stream is not recognized ++ */ ++int FontConfig_OHOS::loadFont(const SkFontScanner& scanner, const char* fname) ++{ ++ std::unique_ptr stream = SkStream::MakeFromFile(fname); ++ int count = 1; ++ SkFontScanner::AxisDefinitions axisDefs; ++ FontInfo font(fname, 0); ++ int ttcIndex = 0; ++ if (ttcIndexMap.find(font.familyName) != nullptr) { ++ ttcIndex = ttcIndexMap.find(font.familyName)->ttcIndex; ++ } ++ if (stream == nullptr || ++ scanner.scanFile(stream.get(), &count) == false || ++ scanner.scanInstance( ++ stream.get(), ++ ttcIndex, ++ 0, ++ &font.familyName, ++ &font.style, ++ &font.isFixedWidth, ++ &axisDefs) == false) { ++ int err = NO_ERROR; ++ if (stream == nullptr) { ++ err = ERROR_FONT_NOT_EXIST; ++ } else { ++ err = ERROR_FONT_INVALID_STREAM; ++ } ++ SkDEBUGF("load font %s : %s\n", errToString(err), fname); ++ char* fnameCopy = strdup(fname); ++ errSet.emplace_back(err, basename(fnameCopy)); ++ free(fnameCopy); ++ return err; ++ } ++ // for adjustMap - update weight ++ if (adjustMap.find(font.familyName) != nullptr) { ++ const std::vector adjustSet = *(adjustMap.find(font.familyName)); ++ for (unsigned int i = 0; i < adjustSet.size(); i++) { ++ if (font.style.weight() == adjustSet[i].origValue) { ++ font.style = SkFontStyle(adjustSet[i].newValue, font.style.width(), font.style.slant()); ++ break; ++ } ++ } ++ } ++ bool ret = false; ++ if (count > 1) { ++ ret = insertTtcFont(count, font); ++ } else if (axisDefs.size() > 0) { ++ ret = insertVariableFont(axisDefs, font); ++ } ++ if (!ret) { ++ SkString specifiedName; ++ TypefaceSet* tpSet = getTypefaceSet(font.familyName, specifiedName); ++ if (tpSet) { ++ sk_sp typeface = sk_make_sp(specifiedName, font); ++ tpSet->push_back(std::move(typeface)); ++ } ++ } ++ return NO_ERROR; ++} ++ ++/*! To scan the system font directories ++ * \param fontScanner the scanner used to parse a font file ++ * \return NO_ERROR success ++ * \return ERROR_DIR_NOT_FOUND a font directory is not exist ++ */ ++int FontConfig_OHOS::scanFonts(const SkFontScanner& fontScanner) ++{ ++ int err = NO_ERROR; ++ if (fontDirSet.size() == 0) { ++ fontDirSet.emplace_back(SkString("/system/fonts/")); ++ } ++ for (unsigned int i = 0; i < fontDirSet.size(); i++) { ++ DIR* dir = opendir(fontDirSet[i].c_str()); ++ if (dir == nullptr) { ++ err = logErrInfo(ERROR_DIR_NOT_FOUND, fontDirSet[i].c_str()); ++ continue; ++ } ++ struct dirent* node = nullptr; ++#if defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_WIN) ++ struct stat filestat; ++#endif ++ while ((node = readdir(dir))) { ++#if defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_WIN) ++ stat(node->d_name, &filestat); ++ if(S_ISDIR(filestat.st_mode)) { ++ continue; ++ } ++#else ++ if (node->d_type != DT_REG) { ++ continue; ++ } ++#endif ++ const char* fname = node->d_name; ++ int len = strlen(fname); ++ int suffixLen = strlen(".ttf"); ++ if (len < suffixLen || (strncmp(fname + len - suffixLen, ".ttf", suffixLen) && ++ strncmp(fname + len - suffixLen, ".otf", suffixLen) && ++ strncmp(fname + len - suffixLen, ".ttc", suffixLen) && ++ strncmp(fname + len - suffixLen, ".otc", suffixLen))) { ++ continue; ++ } ++ len += (fontDirSet[i].size() + 2); // 2 more characters for '/' and '\0' ++ char fullname[len]; ++ memset(fullname, 0, len); ++ strncpy(fullname, fontDirSet[i].c_str(), fontDirSet[i].size()); ++#if defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_WIN) ++ if (fontDirSet[i][fontDirSet[i].size() - 1] != '\\') { ++ strcat(fullname, "\\"); ++ } ++#else ++ if (fontDirSet[i][fontDirSet[i].size() - 1] != '/') { ++ strcat(fullname, "/"); ++ } ++#endif ++ strcat(fullname, fname); ++ loadFont(fontScanner, fullname); ++ } ++ closedir(dir); ++ } ++ fontDirSet.clear(); ++ return err; ++} ++ ++/*! To reset the generic family ++ * \n 1. To sort the typefaces for each font style set in generic list ++ * \n 2. To build typeface set for those font style sets which have single weight value ++ */ ++void FontConfig_OHOS::resetGenericValue() ++{ ++ aliasMap.foreach([this](const std::pair>& pair) { ++ const std::vector& aliasSet = pair.second; ++ int index = aliasSet[0].pos; ++ if (genericFamilySet[index]->typefaceSet->size() == 0) { ++ SkDEBUGF("resetGenericValue ERROR_FAMILY_NOT_FOUND1 %s", pair.first.c_str()); ++ this->logErrInfo(ERROR_FAMILY_NOT_FOUND, pair.first.c_str()); ++ } else { ++ sortTypefaceSet(genericFamilySet[index]->typefaceSet); ++ for (unsigned int i = 1; i < aliasSet.size(); i++) { ++ if (aliasSet[i].weight == 0) { ++ continue; ++ } ++ buildSubTypefaceSet(genericFamilySet[index]->typefaceSet, ++ genericFamilySet[index + i]->typefaceSet, ++ genericFamilySet[index + i]->familyName, ++ aliasSet[i].weight); ++ if (genericFamilySet[index + i]->typefaceSet->size() == 0) { ++ SkDEBUGF("resetGenericValue ERROR_FAMILY_NOT_FOUND2 %s", genericFamilySet[index + i]->familyName.c_str()); ++ this->logErrInfo(ERROR_FAMILY_NOT_FOUND, ++ genericFamilySet[index + i]->familyName.c_str()); ++ } ++ } ++ } ++ }); ++ ++ aliasMap.reset(); ++ adjustMap.reset(); ++ variationMap.reset(); ++ ttcIndexMap.reset(); ++} ++ ++/*! To build a sub typeface set according to weight from a typeface set ++ * \param typefaceSet the parent typeface set ++ * \param[out] subSet the sub typeface set returned to the caller ++ * \param familyName the family name of the sub typeface set ++ * \param weight the weight of the sub typeface set ++ */ ++void FontConfig_OHOS::buildSubTypefaceSet(const std::shared_ptr& typefaceSet, ++ std::shared_ptr& subSet, const SkString& familyName, int weight) ++{ ++ if (typefaceSet->size() == 0) { ++ return; ++ } ++ for (unsigned int i = 0; i < typefaceSet->size(); i++) { ++ const SkTypeface_OHOS* typeface = (*typefaceSet)[i].get(); ++ if (typeface && typeface->fontStyle().weight() == weight) { ++ const FontInfo* pFont = typeface->getFontInfo(); ++ if (pFont == nullptr) { ++ continue; ++ } ++ FontInfo font(*pFont); ++ sk_sp newTypeface = sk_make_sp(familyName, font); ++ subSet->push_back(std::move(newTypeface)); ++ } ++ } ++} ++ ++/*! To reset the fallback value ++ * \n To sort the typefaces for each font style set in fallback list. ++ */ ++void FontConfig_OHOS::resetFallbackValue() ++{ ++ for (unsigned int i = 0; i < fallbackSet.size(); i++) { ++ if (fallbackSet[i]->typefaceSet->size() == 0) { ++ SkDEBUGF("resetFallbackValue ERROR_FAMILY_NOT_FOUND %s", fallbackSet[i]->familyName.c_str()); ++ ++ logErrInfo(ERROR_FAMILY_NOT_FOUND, fallbackSet[i]->familyName.c_str()); ++ } ++ sortTypefaceSet(fallbackSet[i]->typefaceSet); ++ } ++} ++ ++/*! To check if an error happened ++ * \param err the id of an error ++ * \param text the key to indicate the part with the error happened ++ * \return false, this kind of error did not happen ++ * \return true, the error happened ++ */ ++bool FontConfig_OHOS::hasError(int err, const SkString& text) const ++{ ++ for (unsigned int i = 0; i < errSet.size(); i++) { ++ if (errSet[i].err == err && errSet[i].text == text) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++/*! To get the total count of errors happened ++ * \return The count of errors ++ */ ++int FontConfig_OHOS::getErrorCount() const ++{ ++ return errSet.size(); ++} ++ ++/*! To sort the typeface set ++ * \param typefaceSet the typeface set to be sorted ++ */ ++void FontConfig_OHOS::sortTypefaceSet(std::shared_ptr& typefaceSet) ++{ ++ if (typefaceSet.get() == nullptr || typefaceSet->size() <= 1) { ++ return; ++ } ++ TypefaceSet& tpSet = *(typefaceSet.get()); ++ for (unsigned int i = 0; i < tpSet.size(); i++) ++ for (unsigned int j = 0; j < tpSet.size() - 1; j++) { ++ if ((tpSet[j]->fontStyle().weight() > tpSet[j + 1]->fontStyle().weight()) || ++ (tpSet[j]->fontStyle().weight() == tpSet[j + 1]->fontStyle().weight() && ++ tpSet[j]->fontStyle().slant() > tpSet[j + 1]->fontStyle().slant())) { ++ tpSet[j].swap(tpSet[j + 1]); ++ } ++ } ++} ++ ++/*! To get the display text of an error ++ * \param err the id of an error ++ * \return The text to explain the error ++ */ ++const char* FontConfig_OHOS::errToString(int err) ++{ ++ const static std::array errToString{ ++ "successful", // NO_ERROR = 0 ++ "config file is not found", // ERROR_CONFIG_NOT_FOUND ++ "the format of config file is not supported", // ERROR_CONFIG_FORMAT_NOT_SUPPORTED ++ "missing tag", // ERROR_CONFIG_MISSING_TAG ++ "invalid value type", // ERROR_CONFIG_INVALID_VALUE_TYPE ++ "font file is not exist", // ERROR_FONT_NOT_EXIST ++ "invalid font stream", // ERROR_FONT_INVALID_STREAM ++ "no font stream", // ERROR_FONT_NO_STREAM ++ "family is not found", // ERROR_FAMILY_NOT_FOUND ++ "no available family in the system", //ERROR_NO_AVAILABLE_FAMILY ++ "no such directory" // ERROR_DIR_NOT_FOUND ++ }; ++ if (err >= 0 && err < ERROR_TYPE_COUNT) { ++ return errToString[err]; ++ } ++ return "unknown error"; ++} ++ ++/*! To log the error information ++ * \param err the id of an error ++ * \param key the key which indicates the the part with the error ++ * \param expected the expected type of json node. ++ * \n It's used only for err 'ERROR_CONFIG_INVALID_VALUE_TYPE' ++ * \param actual the actual type of json node. ++ * \n It's used only for err 'ERROR_CONFIG_INVALID_VALUE_TYPE' ++ * \return err ++ */ ++int FontConfig_OHOS::logErrInfo(int err, const char* key, Json::ValueType expected, ++ Json::ValueType actual) ++{ ++ errSet.emplace_back(err, key); ++ if (err != ERROR_CONFIG_INVALID_VALUE_TYPE) { ++ SkDEBUGF("%s : %s\n", errToString(err), key); ++ } else { ++ const char* types[] = { ++ "null", ++ "int", ++ "unit", ++ "real", ++ "string", ++ "boolean", ++ "array", ++ "object", ++ }; ++ int size = sizeof(types) / sizeof(char*); ++ if ((expected >= 0 && expected < size) && ++ (actual >= 0 && actual < size)) { ++ SkDEBUGF("%s : '%s' should be '%s', but here it's '%s'\n", ++ errToString(err), key, types[expected], types[actual]); ++ } else { ++ SkDEBUGF("%s : %s\n", errToString(err), key); ++ } ++ } ++ return err; ++} ++ ++bool FontConfig_OHOS::judgeFileExist() ++{ ++ bool haveFile = false; ++ for (unsigned int i = 0; i < fontDirSet.size(); i++) { ++ DIR* dir = opendir(fontDirSet[i].c_str()); ++ if (dir == nullptr) { ++ logErrInfo(ERROR_DIR_NOT_FOUND, fontDirSet[i].c_str()); ++ continue; ++ } ++ struct dirent* node = nullptr; ++#if defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_WIN) ++ struct stat fileStat; ++#endif ++ while ((node = readdir(dir))) { ++#if defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_WIN) ++ stat(node->d_name, &fileStat); ++ if (S_ISDIR(fileStat.st_mode)) { ++ continue; ++ } ++#else ++ if (node->d_type != DT_REG) { ++ continue; ++ } ++#endif ++ const char* fileName = node->d_name; ++ int len = strlen(fileName); ++ int suffixLen = strlen(".ttf"); ++ if (len < suffixLen || (strncmp(fileName + len - suffixLen, ".ttf", suffixLen) && ++ strncmp(fileName + len - suffixLen, ".otf", suffixLen) && ++ strncmp(fileName + len - suffixLen, ".ttc", suffixLen) && ++ strncmp(fileName + len - suffixLen, ".otc", suffixLen))) { ++ continue; ++ } ++ haveFile = true; ++ break; ++ } ++ (void)closedir(dir); ++ if (haveFile) { ++ break; ++ } ++ } ++ return haveFile; ++} ++ ++int FontConfig_OHOS::checkProductFile(const char* fname) ++{ ++ int err = parseConfig(PRODUCT_DEFAULT_CONFIG); ++ SkDebugf("parse productfontconfig json file err = %d", err); ++ if ((err != NO_ERROR) || (!judgeFileExist())) { ++ SkDebugf("parse productfontconfig json file error"); ++ fontDirSet.clear(); ++ fallbackForMap.reset(); ++ genericFamilySet.clear(); ++ fallbackSet.clear(); ++ genericNames.reset(); ++ fallbackNames.reset(); ++ errSet.clear(); ++ aliasMap.reset(); ++ adjustMap.reset(); ++ variationMap.reset(); ++ ttcIndexMap.reset(); ++ err = parseConfig(fname); ++ } ++ return err; ++} ++ ++void FontConfig_OHOS::setSystemDefaultFont(sk_sp typeface) { ++ std::unique_ptr familySet = std::make_unique(); ++ typeface->getFamilyName(&(familySet->familyName)); ++ familySet->typefaceSet = std::make_shared(); ++ ++ SkTypeface_OHOS* typeface_ = (SkTypeface_OHOS* )(typeface.get()); ++ FontInfo info = FontInfo(*(typeface_->getFontInfo())); ++ familySet->typefaceSet->push_back(sk_make_sp(info)); ++ ++ genericFamilySet.insert(genericFamilySet.begin(), std::move(familySet)); ++} +diff --git a/src/ports/skia_ohos/FontConfig_ohos.h b/src/ports/skia_ohos/FontConfig_ohos.h +new file mode 100644 +index 0000000000..d53ce299cf +--- /dev/null ++++ b/src/ports/skia_ohos/FontConfig_ohos.h +@@ -0,0 +1,219 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef FONTCONFIG_OHOS_H ++#define FONTCONFIG_OHOS_H ++ ++#include "../vulkan-deps/spirv-headers/src/tools/buildHeaders/jsoncpp/dist/json/json.h" ++#include ++ ++#include "src/core/SkFontDescriptor.h" ++#include "src/ports/SkFontHost_FreeType_common.h" ++#include "include/core/SkFontStyle.h" ++#include "include/core/SkStream.h" ++#include "include/core/SkString.h" ++#include "include/core/SkTypes.h" ++#include "src/core/SkTHash.h" ++ ++#include "FontInfo_ohos.h" ++#include "SkTypeface_ohos.h" ++ ++struct FontInfo; ++struct FallbackInfo; ++struct GenericFamily; ++struct FallbackSetPos; ++ ++using TypefaceSet = std::vector>; ++using GenericFamilySet = std::vector>; ++using FallbackSet = std::vector>; ++using FallbackForMap = skia_private::THashMap; ++using NamesMap = skia_private::THashMap; ++using Coordinate = SkFontArguments::VariationPosition::Coordinate; ++using AxisDefinitions = SkFontScanner::AxisDefinitions; ++ ++/*! ++ * Error code definition ++ */ ++namespace ErrorCode { ++ ++enum { ++ NO_ERROR = 0, // no error ++ ERROR_CONFIG_NOT_FOUND, // the configuration document is not found ++ ERROR_CONFIG_FORMAT_NOT_SUPPORTED, // the formation of configuration is not supported ++ ERROR_CONFIG_MISSING_TAG, // missing tag in the configuration ++ ERROR_CONFIG_INVALID_VALUE_TYPE, // invalid value type in the configuration ++ ERROR_FONT_NOT_EXIST, // the font file is not exist ++ ERROR_FONT_INVALID_STREAM, // the stream is not recognized ++ ERROR_FONT_NO_STREAM, // no stream in the font data ++ ERROR_FAMILY_NOT_FOUND, // the family name is not found in the system ++ ERROR_NO_AVAILABLE_FAMILY, // no available family in the system ++ ERROR_DIR_NOT_FOUND, // the directory is not exist ++ ++ ERROR_TYPE_COUNT, ++}; ++ ++} /* namespace ErrorCode */ ++ ++/*! ++ * \brief To manage the related information of a 'fallbackFor' family name ++ */ ++struct FallbackSetPos { ++ unsigned int index; // the index of the first font style set in the fallback set for a specified family name ++ unsigned int count; // the count of font style sets for a specified family name ++}; ++ ++/*! ++ * \brief To manage the information for a generic family item ++ */ ++struct GenericFamily { ++ SkString familyName; // the specified family name of the font style set ++ std::shared_ptr typefaceSet; // the typeface set of the font style set ++ virtual ~GenericFamily() = default; ++}; ++ ++/*! ++ * \brief To manage the information for a fallback family item ++ */ ++struct FallbackInfo : GenericFamily { ++ SkString langs; // the language for which the font style set is ++}; ++ ++/*! ++ * \brief To parse the font configuration document and manage the system fonts ++ */ ++class FontConfig_OHOS { ++public: ++ explicit FontConfig_OHOS(const SkFontScanner& fontScanner, ++ const char* fname = nullptr); ++ virtual ~FontConfig_OHOS() = default; ++ const FallbackForMap& getFallbackForMap() const; ++ const FallbackSet& getFallbackSet() const; ++ int getFamilyCount() const; ++ int getDefaultFamily(SkString* familyName) const; ++ int getFamilyName(int index, SkString* familyName) const; ++ int getTypefaceCount(int styleIndex, bool isFallback = false) const; ++ int getStyleIndex(const char* familyName, bool& isFallback) const; ++ ++ sk_sp getTypeface(int styleIndex, int index, bool isFallback = false) const; ++ sk_sp getTypeface(int styleIndex, const SkFontStyle& style, ++ bool isFallback = false) const; ++ ++#if ENABLE_DEBUG ++ void dumpFont(const FontInfo& font) const; ++ void dumpGeneric() const; ++ void dumpFallback() const; ++#endif ++ bool hasError(int err, const SkString& text) const; ++ int getErrorCount() const; ++ ++ static sk_sp matchFontStyle(const TypefaceSet& typefaceSet, const SkFontStyle& pattern); ++ ++ static const char* errToString(int err); ++ void setSystemDefaultFont(sk_sp typeface); ++private: ++ struct AliasInfo; ++ struct AdjustInfo; ++ struct VariationInfo; ++ struct TtcIndexInfo; ++ using AliasMap = skia_private::THashMap>; ++ using AjdustMap = skia_private::THashMap>; ++ using VariationMap = skia_private::THashMap>; ++ using TtcIndexMap = skia_private::THashMap; ++ ++ /*! ++ * \brief To manage the adjust information ++ */ ++ struct AdjustInfo { ++ int origValue; // the real value of the font weight ++ int newValue; // the specified value of weight for a font ++ }; ++ ++ /*! ++ * \brief To manage the alias information of ++ */ ++ struct AliasInfo { ++ int pos; // the index of a font style set in generic family list. ++ int weight; // the weight of the font style set. 0 means no specified weight ++ }; ++ ++ /*! ++ * \brief To manage the variation information ++ */ ++ struct VariationInfo { ++ VariationInfo() : weight(-1), width(-1), slant(-1){} ++ std::vector axis; // the axis set such as 'wght', 'wdth' and 'slnt'. ++ int weight; // the value of mapping weight ++ int width; // the value of mapping width ++ int slant; // the value of mapping slant ++ }; ++ ++ /*! ++ * \brief To manage the 'index' information for ttc fonts ++ */ ++ struct TtcIndexInfo { ++ SkString familyName; // the family name of the first typeface in a ttc font ++ int ttcIndex; // the index of a typeface in a ttc font ++ }; ++ ++ /*! ++ * \brief To manage the information of errors happened ++ */ ++ struct ErrorInfo { ++ ErrorInfo(int err, const char* text) : err(err), text(SkString(text)){} ++ ErrorInfo(int err, SkString& text) : err(err), text(std::move(text)){} ++ int err; // error id ++ SkString text; // the part with error ++ }; ++ ++ std::vector fontDirSet; // the directories where the fonts are ++ ++ FallbackForMap fallbackForMap; // a hash table to save the fallbackFor pairs ++ GenericFamilySet genericFamilySet; // the font style set list of generic family ++ FallbackSet fallbackSet; // the font style set list of fallback family ++ ++ NamesMap genericNames; // a map to store the index of a family for generic family ++ NamesMap fallbackNames; // a map to store the index of a family for fallback family ++ ++ std::vector errSet; // the errors happened ++ AliasMap aliasMap; // to save alias information temporarily ++ AjdustMap adjustMap; // to save adjust information temporarily ++ VariationMap variationMap; // to save variation information temporarily ++ TtcIndexMap ttcIndexMap; // to save 'index' information temporarily ++ ++ int parseConfig(const char* fname); ++ int checkConfigFile(const char* fname, Json::Value& root); ++ int parseFontDir(const Json::Value& root); ++ int parseGeneric(const Json::Value& root); ++ int parseFallback(const Json::Value& root); ++ int parseFallbackItem(const Json::Value& root); ++ int parseAlias(const Json::Value& root, std::vector& aliasSet); ++ int parseAdjust(const Json::Value& root, std::vector& adjustSet); ++ int parseVariation(const Json::Value& root, std::vector& variationSet); ++ int parseTtcIndex(const Json::Value& root, const SkString& familyName); ++ void getAxisValues(const AxisDefinitions& axisDefinitions, ++ const VariationInfo& variation, FontInfo& font) const; ++ bool insertTtcFont(int count, FontInfo& font); ++ bool insertVariableFont(const AxisDefinitions& axisDefinitions, FontInfo& font); ++ TypefaceSet* getTypefaceSet(const SkString& familyName, SkString& specifiedName) const; ++ ++ int loadFont(const SkFontScanner& scanner, const char* fname); ++ int scanFonts(const SkFontScanner& fontScanner); ++ void resetGenericValue(); ++ void buildSubTypefaceSet(const std::shared_ptr& typefaceSet, ++ std::shared_ptr& subSet, const SkString& familyName, int weight); ++ void resetFallbackValue(); ++ int logErrInfo(int err, const char* key, Json::ValueType expected = Json::nullValue, ++ Json::ValueType actual = Json::nullValue); ++ static void sortTypefaceSet(std::shared_ptr& typefaceSet); ++ static uint32_t getFontStyleDifference(const SkFontStyle& style1, const SkFontStyle& style2); ++ static char* getFileData(const char* fname, int& size); ++ FontConfig_OHOS(const FontConfig_OHOS&) = delete; ++ FontConfig_OHOS& operator = (const FontConfig_OHOS&) = delete; ++ FontConfig_OHOS(FontConfig_OHOS&&) = delete; ++ FontConfig_OHOS& operator = (FontConfig_OHOS&&) = delete; ++ int checkProductFile(const char* fname); ++ bool judgeFileExist(); ++}; ++ ++#endif /* FONTCONFIG_OHOS_H */ +diff --git a/src/ports/skia_ohos/FontInfo_ohos.h b/src/ports/skia_ohos/FontInfo_ohos.h +new file mode 100644 +index 0000000000..66b1424333 +--- /dev/null ++++ b/src/ports/skia_ohos/FontInfo_ohos.h +@@ -0,0 +1,148 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef FONTINFO_OHOS_H ++#define FONTINFO_OHOS_H ++ ++#include ++ ++#include "include/private/base/SkFixed.h" ++#include "src/core/SkFontScanner.h" ++#include "src/core/SkFontDescriptor.h" ++#include "src/ports/SkFontHost_FreeType_common.h" ++ ++/*! ++ * \brief To manage the font information ++ */ ++struct FontInfo { ++public: ++ /*! Constructor ++ * ++ */ ++ FontInfo() : familyName(""), fname(""), index(0), ++ style(SkFontStyle::Normal()), isFixedWidth(false), stream(nullptr) ++ { ++ memset(&axisSet, 0, sizeof(AxisSet)); ++ } ++ /*! Copy Constructor ++ * \param font an object of FontInfo ++ */ ++ explicit FontInfo(const FontInfo& font) ++ : familyName(font.familyName), fname(font.fname), index(font.index), ++ style(font.style), isFixedWidth(font.isFixedWidth), stream(nullptr) ++ { ++ axisSet.axis = font.axisSet.axis; ++ axisSet.range = font.axisSet.range; ++ if (font.stream) { ++ stream = font.stream->duplicate(); ++ } ++ } ++ ++ /*! Move Constructor ++ * \param font an object of FontInfo ++ */ ++ explicit FontInfo(FontInfo&& font) ++ : familyName(std::move(font.familyName)), fname(std::move(font.fname)), index(font.index), ++ style(font.style), isFixedWidth(font.isFixedWidth), stream(nullptr) ++ { ++ axisSet.axis = std::move(font.axisSet.axis); ++ axisSet.range = std::move(font.axisSet.range); ++ if (font.stream) { ++ stream = std::move(font.stream); ++ } ++ } ++ ++ /*! Constructor ++ * \param fname the fullname of font file ++ * \param index the index of the typeface in the font file ++ */ ++ FontInfo(const char* fname, int index) ++ : familyName(""), fname(""), index(index), ++ style(SkFontStyle::Normal()), isFixedWidth(false), stream(nullptr) ++ { ++ if (fname) { ++ this->fname.set(fname); ++ } ++ memset(&axisSet, 0, sizeof(axisSet)); ++ } ++ ++ /*! Destructor ++ * ++ */ ++ virtual ~FontInfo() = default; ++ ++ /*! Copy assignment operator ++ * \param font an object of FontInfo ++ */ ++ FontInfo& operator = (const FontInfo& font) ++ { ++ if (this == &font) { ++ return *this; ++ } ++ familyName = font.familyName; ++ fname = font.fname; ++ index = font.index; ++ style = font.style; ++ isFixedWidth = font.isFixedWidth; ++ axisSet.axis = font.axisSet.axis; ++ axisSet.range = font.axisSet.range; ++ if (font.stream) { ++ stream = font.stream->duplicate(); ++ } ++ return *this; ++ } ++ ++ /*! The move assignment operator ++ * \param font an object of FontInfo ++ */ ++ FontInfo& operator = (FontInfo&& font) ++ { ++ if (this == &font) { ++ return *this; ++ } ++ familyName = std::move(font.familyName); ++ fname = std::move(font.fname); ++ index = font.index; ++ style = font.style; ++ isFixedWidth = font.isFixedWidth; ++ axisSet.axis = std::move(font.axisSet.axis); ++ axisSet.range = std::move(font.axisSet.range); ++ if (font.stream) { ++ stream = std::move(font.stream); ++ } ++ return *this; ++ } ++ ++ /*! To set axis values ++ * \param count the count of axis ++ * \param axis an array of SkFixed value ++ * \param range an array of AxisDefinition ++ */ ++ void setAxisSet(int count, const SkFixed* axis, ++ const SkFontScanner::AxisDefinition* range) ++ { ++ axisSet.axis.clear(); ++ axisSet.range.clear(); ++ for (int i = 0; i < count; i++) { ++ axisSet.axis.emplace_back(axis[i]); ++ axisSet.range.emplace_back(range[i]); ++ } ++ } ++ ++ SkString familyName; // the real family name of the font ++ SkString fname; // the full name of font file ++ int index; // the index of the font in a ttc font ++ SkFontStyle style; // the font style ++ bool isFixedWidth; // the flag to indicate if the font has fixed width or not ++ /*! ++ * \brief To manage the axis values for variable font ++ */ ++ struct AxisSet { ++ std::vector axis; // the axis values ++ std::vector range; // the axis ranges ++ } axisSet; // the axis values for a variable font ++ std::unique_ptr stream; // the data stream of font file ++}; ++ ++#endif /* FONTINFO_OHOS_H */ +diff --git a/src/ports/skia_ohos/SkFontMgr_ohos.cpp b/src/ports/skia_ohos/SkFontMgr_ohos.cpp +new file mode 100644 +index 0000000000..b36c1fe2d0 +--- /dev/null ++++ b/src/ports/skia_ohos/SkFontMgr_ohos.cpp +@@ -0,0 +1,445 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "SkFontMgr_ohos.h" ++ ++#include "SkTypeface_ohos.h" ++#include "SkRefCnt.h" ++#include "include/private/base/SkDebug.h" ++ ++using namespace ErrorCode; ++ ++/*! Constructor ++ * \param path the full path of system font configuration document ++ */ ++SkFontMgr_OHOS::SkFontMgr_OHOS(const char* path) ++{ ++ fontConfig = std::make_shared(fontScanner, path); ++ familyCount = fontConfig->getFamilyCount(); ++} ++ ++/*! To get the count of families ++ * \return The count of families in the system ++ */ ++int SkFontMgr_OHOS::onCountFamilies() const ++{ ++ return familyCount; ++} ++ ++/*! To get the family name for a font style set ++ * \param index the index of a font style set ++ * \param[out] familyName the family name returned to the caller ++ * \n The family name will be reset to "", if index is out of range ++ */ ++void SkFontMgr_OHOS::onGetFamilyName(int index, SkString* familyName) const ++{ ++ if (fontConfig == nullptr || familyName == nullptr) { ++ return; ++ } ++ fontConfig->getFamilyName(index, familyName); ++} ++ ++/*! To create an object of SkFontStyleSet ++ * \param index the index of a font style set ++ * \return The pointer of SkFontStyleSet ++ * \n Return null, if index is out of range ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++sk_sp SkFontMgr_OHOS::onCreateStyleSet(int index) const ++{ ++ if (fontConfig == nullptr) { ++ return nullptr; ++ } ++ if (index < 0 || index >= this->countFamilies()) { ++ return nullptr; ++ } ++ return sk_ref_sp(new SkFontStyleSet_OHOS(fontConfig, index)); ++} ++ ++/*! To get a matched object of SkFontStyleSet ++ * \param familyName the family name of a font style set ++ * \return The pointer of SkFontStyleSet ++ * \n Return the default font style set, if family name is null ++ * \n Return null, if family name is not found ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++sk_sp SkFontMgr_OHOS::onMatchFamily(const char familyName[]) const ++{ ++ if (fontConfig == nullptr) { ++ SkDEBUGF("onMatchFamily get null fontConfig"); ++ return nullptr; ++ } ++ SkDEBUGF("onMatchFamily get familyName %s", familyName); ++ // return default system font when familyName is null ++ if (familyName == nullptr) { ++ SkDEBUGF("onMatchFamily get null familyName"); ++ return sk_ref_sp(new SkFontStyleSet_OHOS(fontConfig, 0)); ++ } ++ ++ bool isFallback = false; ++ int index = fontConfig->getStyleIndex(familyName, isFallback); ++ if (index == -1) { ++ SkDEBUGF("onMatchFamily get -1 index"); ++ return nullptr; ++ } ++ SkDEBUGF("onMatchFamily get index %d", index); ++ return sk_ref_sp(new SkFontStyleSet_OHOS(fontConfig, index, isFallback)); ++} ++ ++/*! To get a matched typeface ++ * \param familyName the family name of a font style set ++ * \param style the font style to be matched ++ * \return An object of typeface which is closest matching to 'style' ++ * \n Return the typeface in the default font style set, if family name is null ++ * \n Return null, if family name is not found ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++sk_sp SkFontMgr_OHOS::onMatchFamilyStyle(const char familyName[], const SkFontStyle& style) const ++{ ++ if (fontConfig == nullptr) { ++ return nullptr; ++ } ++ bool isFallback = false; ++ int styleIndex = 0; ++ if (familyName) { ++ styleIndex = fontConfig->getStyleIndex(familyName, isFallback); ++ } ++ return fontConfig->getTypeface(styleIndex, style, isFallback); ++} ++ ++/*! To get a matched typeface ++ * \n Use the system fallback to find a typeface for the given character. ++ * \param familyName the family name which the typeface is fallback For ++ * \param style the font style to be matched ++ * \param bcp47 an array of languages which indicate the language of 'character' ++ * \param bcp47Count the array size of bcp47 ++ * \param character a UTF8 value to be matched ++ * \return An object of typeface which is for the given character ++ * \return Return the typeface in the default fallback set, if familyName is null ++ * \return Return null, if the typeface is not found for the given character ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++sk_sp SkFontMgr_OHOS::onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle& style, ++ const char* bcp47[], int bcp47Count, SkUnichar character) const ++{ ++ if (fontConfig == nullptr) { ++ return nullptr; ++ } ++ const FallbackForMap& fallbackForMap = fontConfig->getFallbackForMap(); ++ const FallbackSet& fallbackSet = fontConfig->getFallbackSet(); ++ SkString defaultFamily(""); ++ SkString key = defaultFamily; ++ FallbackSetPos* item = nullptr; ++ if (familyName == nullptr) { ++ item = fallbackForMap.find(defaultFamily); ++ } else { ++ item = fallbackForMap.find(SkString(familyName)); ++ if (item) { ++ key = SkString(familyName); ++ } else { ++ item = fallbackForMap.find(defaultFamily); ++ } ++ } ++ if (item == nullptr) { ++ SkDEBUGF("%s : '%s' must be a fallback key in the config file\n", ++ FontConfig_OHOS::errToString(ERROR_FAMILY_NOT_FOUND), defaultFamily.c_str()); ++ return nullptr; ++ } ++ while (true) { ++ if (bcp47Count > 0) { ++ sk_sp retTp = findTypeface(*item, style, bcp47, bcp47Count, character); ++ if (retTp) { ++ return retTp; ++ } ++ if (key == defaultFamily) { ++ bcp47Count = 0; ++ continue; ++ } ++ item = fallbackForMap.find(defaultFamily); ++ key = defaultFamily; ++ } else { ++ for (unsigned int i = item->index; i < item->index + item->count && i < fallbackSet.size(); i++) { ++ const TypefaceSet& tpSet = *(fallbackSet[i]->typefaceSet.get()); ++ if (tpSet.size() > 0 && tpSet[0]->unicharToGlyph(character) != 0) { ++ sk_sp typeface = FontConfig_OHOS::matchFontStyle(tpSet, style); ++ return typeface; ++ } ++ } ++ if (key == defaultFamily) { ++ break; ++ } ++ item = fallbackForMap.find(defaultFamily); ++ key = defaultFamily; ++ } ++ } ++ return nullptr; ++} ++ ++/*! To find the matched typeface for the given parameters ++ * \n Use the system fallback to find a typeface for the given character. ++ * \param fallbackItem the fallback items in which to find the typeface ++ * \param style the font style to be matched ++ * \param bcp47 an array of languages which indicate the language of 'character' ++ * \param bcp47Count the array size of bcp47 ++ * \param character a UTF8 value to be matched ++ * \return An object of typeface which is for the given character ++ * \return Return null, if the typeface is not found for the given character ++ */ ++sk_sp SkFontMgr_OHOS::findTypeface(const FallbackSetPos& fallbackItem, const SkFontStyle& style, ++ const char* bcp47[], int bcp47Count, SkUnichar character) const ++{ ++ if (bcp47Count == 0) { ++ return nullptr; ++ } ++ ++ const FallbackSet& fallbackSet = fontConfig->getFallbackSet(); ++ // example bcp47 code : 'zh-Hans' : ('zh' : iso639 code, 'Hans' : iso15924 code) ++ // iso639 code will be taken from bcp47 code, so that we can try to match ++ // bcp47 or only iso639. Therefore totalCount need to be 'bcp47Count * 2' ++ int totalCount = bcp47Count * 2; ++ int tps[totalCount]; ++ for (int i = 0; i < totalCount; i++) { ++ tps[i] = -1; ++ } ++ // find the families matching the bcp47 list ++ for (unsigned int i = fallbackItem.index; i < fallbackItem.index + fallbackItem.count ++ && i < fallbackSet.size(); i++) { ++ int ret = compareLangs(fallbackSet[i]->langs, bcp47, bcp47Count, tps); ++ if (ret == -1) { ++ continue; ++ } ++ tps[ret] = i; ++ } ++ // match typeface in families ++ for (int i = bcp47Count - 1; i >= 0; i--) { ++ if (tps[i] == -1) { ++ continue; ++ } ++ const TypefaceSet& tpSet = *(fallbackSet[tps[i]]->typefaceSet.get()); ++ if (tpSet.size() > 0 && tpSet[0]->unicharToGlyph(character) != 0) { ++ sk_sp typeface = FontConfig_OHOS::matchFontStyle(tpSet, style); ++ return typeface; ++ } ++ } ++ for (int i = totalCount - 1; i >= bcp47Count; i--) { ++ if (tps[i] == -1) { ++ continue; ++ } ++ const TypefaceSet& tpSet = *(fallbackSet[tps[i]]->typefaceSet.get()); ++ if (tpSet.size() > 0 && tpSet[0]->unicharToGlyph(character) != 0) { ++ sk_sp typeface = FontConfig_OHOS::matchFontStyle(tpSet, style); ++ return typeface; ++ } ++ } ++ return nullptr; ++} ++ ++/*! To compare the languages of an typeface with a bcp47 list ++ * \param langs the supported languages by an typeface ++ * \param bcp47 the array of bcp47 language to be matching ++ * \param bcp47Count the array size of bcp47 ++ * \param tps an array of the index of typeface which is matching one value of bcp47 ++ * \return The index of language in bcp47, if matching happens ++ * \n Return -1, if no language matching happens ++ */ ++int SkFontMgr_OHOS::compareLangs(const SkString& langs, const char* bcp47[], ++ int bcp47Count, const int tps[]) const ++{ ++ /* ++ * zh-Hans : ('zh' : iso639 code, 'Hans' : iso15924 code) ++ */ ++ if (bcp47 == nullptr || bcp47Count == 0) { ++ return -1; ++ } ++ for (int i = bcp47Count - 1; i >= 0; i--) { ++ if (tps[i] != -1) { ++ continue; ++ } ++ if (langs.find(bcp47[i]) != -1) { ++ return i; ++ } else { ++ const char* iso15924 = strrchr(bcp47[i], '-'); ++ if (iso15924 == nullptr) { ++ continue; ++ } ++ iso15924++; ++ int len = iso15924 - 1 - bcp47[i]; ++ SkString country(bcp47[i], len); ++ if (langs.find(iso15924) != -1 || ++ (strncmp(bcp47[i], "und", strlen("und")) && langs.find(country.c_str()) != -1)) { ++ return i + bcp47Count; ++ } ++ } ++ } ++ return -1; ++} ++ ++ ++/*! To create a typeface from the specified data and TTC index ++ * \param data the data to be parsed ++ * \param index the index of typeface. 0 for none ++ * \return The object of typeface, if successful ++ * \n Return null if the data is not recognized. ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++sk_sp SkFontMgr_OHOS::onMakeFromData(sk_sp data, int ttcIndex) const ++{ ++ if (data == nullptr) { ++ return nullptr; ++ } ++ std::unique_ptr memoryStream = std::make_unique(data); ++ SkFontArguments args; ++ args.setCollectionIndex(ttcIndex); ++ return this->makeTypeface(std::move(memoryStream), args, nullptr); ++} ++ ++/*! To create a typeface from the specified stream and TTC index ++ * \param data the stream to be parsed ++ * \param index the index of typeface. 0 for none ++ * \return The object of typeface, if successful ++ * \n Return null if the stream is not recognized. ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++sk_sp SkFontMgr_OHOS::onMakeFromStreamIndex(std::unique_ptr stream, ++ int ttcIndex) const ++{ ++ if (stream == nullptr) { ++ return nullptr; ++ } ++ SkFontArguments args; ++ args.setCollectionIndex(ttcIndex); ++ return this->makeTypeface(std::move(stream), args, nullptr); ++} ++ ++/*! To create a typeface from the specified stream and font arguments ++ * \param data the stream to be parsed ++ * \param args the arguments of font ++ * \return The object of typeface, if successful ++ * \n Return null if the stream is not recognized. ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++sk_sp SkFontMgr_OHOS::onMakeFromStreamArgs(std::unique_ptr stream, ++ const SkFontArguments& args) const ++{ ++ if (stream == nullptr) { ++ return nullptr; ++ } ++ ++ return this->makeTypeface(std::move(stream), args, nullptr); ++} ++ ++/*! To create a typeface from the specified font file and TTC index ++ * \param path the full path of the given font file ++ * \param ttcIndex the index of typeface in a ttc font file. 0 means none. ++ * \return The object of typeface, if successful ++ * \n Return null if the font file is not found or the content of file is not recognized. ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++sk_sp SkFontMgr_OHOS::onMakeFromFile(const char path[], int ttcIndex) const ++{ ++ if (fontConfig == nullptr) { ++ return nullptr; ++ } ++ ++ std::unique_ptr stream = SkStreamAsset::MakeFromFile(path); ++ if (stream == nullptr) { ++ SkDEBUGF("%s : %s\n", FontConfig_OHOS::errToString(ERROR_FONT_NOT_EXIST), path); ++ return nullptr; ++ } ++ SkFontArguments args; ++ args.setCollectionIndex(ttcIndex); ++ return this->makeTypeface(std::move(stream), args, path); ++} ++ ++/*! To get a typeface matching the specified family and style ++ * \param familyName the specified name to be matching ++ * \param style the specified style to be matching ++ * \return The object of typeface which is the closest matching 'style' when the familyName is found ++ * \return Return a typeface from the default family, if familyName is not found ++ * \return Return null, if there is no any typeface in the system ++ * \note The caller must caller unref() on the returned object is it's not null ++ */ ++sk_sp SkFontMgr_OHOS::onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const ++{ ++ sk_sp typeface = this->onMatchFamilyStyle(familyName, style); ++ // if familyName is not found, then try the default family ++ if (typeface == nullptr && familyName != nullptr) { ++ typeface = this->onMatchFamilyStyle(nullptr, style); ++ } ++ ++ if (typeface) { ++ return sk_sp(typeface); ++ } ++ SkDEBUGF("onLegacyMakeTypeface %s\n", FontConfig_OHOS::errToString(ERROR_NO_AVAILABLE_FAMILY)); ++ return nullptr; ++} ++ ++/*! To make a typeface from the specified stream and font arguments ++ * \param stream the specified stream to be parsed to get font information ++ * \param args the arguments of index or axis values ++ * \param path the fullname of font file ++ * \return The object of typeface if successful ++ * \n Return null, if the stream is not recognized ++ */ ++sk_sp SkFontMgr_OHOS::makeTypeface(std::unique_ptr stream, ++ const SkFontArguments& args, const char path[]) const ++{ ++ FontInfo fontInfo; ++ int ttcIndex = args.getCollectionIndex(); ++ int axisCount = args.getVariationDesignPosition().coordinateCount; ++ ++ if (path) { ++ fontInfo.fname.set(path); ++ } ++ if (axisCount == 0) { ++ if (!fontScanner.scanInstance(stream.get(), ttcIndex, 0, &fontInfo.familyName, &fontInfo.style, ++ &fontInfo.isFixedWidth, nullptr)) { ++ SkDEBUGF("%s\n", FontConfig_OHOS::errToString(ERROR_FONT_INVALID_STREAM)); ++ return nullptr; ++ } ++ } else { ++ AxisDefinitions axisDef; ++ if (!fontScanner.scanInstance(stream.get(), ttcIndex, 0, &fontInfo.familyName, &fontInfo.style, ++ &fontInfo.isFixedWidth, &axisDef)) { ++ SkDEBUGF("%s\n", FontConfig_OHOS::errToString(ERROR_FONT_INVALID_STREAM)); ++ return nullptr; ++ } ++ if (axisDef.size() > 0) { ++ SkFixed axis[axisCount]; ++ fontScanner.computeAxisValues(axisDef, args.getVariationDesignPosition(), ++ axis, fontInfo.familyName); ++ fontInfo.setAxisSet(axisCount, axis, axisDef.data()); ++ } ++ } ++ ++ fontInfo.stream = std::move(stream); ++ fontInfo.index = ttcIndex; ++ return sk_make_sp(fontInfo); ++} ++ ++void SkFontMgr_OHOS::AddSystemFont(std::string path) { ++ static SkOnce once; ++ ++ once([this, path]{ ++ sk_sp typeface = this->onMakeFromFile(path.c_str(), 0); ++ if(!typeface) { ++ SkDEBUGF("parse system font failed"); ++ return; ++ } ++ this->fontConfig->setSystemDefaultFont(typeface); ++ }); ++} ++ ++/*! To create SkFontMgr object for Harmony platform ++ * \param fname the full name of system font configuration documents ++ * \return The object of SkFontMgr_OHOS ++ */ ++sk_sp SkFontMgr_New_OHOS(const char* fname) ++{ ++return sk_make_sp(fname); ++} ++ ++sk_sp SkFontMgr_New_OHOS() { ++ return SkFontMgr_New_OHOS("/system/etc/fontconfig.json"); ++} +diff --git a/src/ports/skia_ohos/SkFontMgr_ohos.h b/src/ports/skia_ohos/SkFontMgr_ohos.h +new file mode 100644 +index 0000000000..c0a8379174 +--- /dev/null ++++ b/src/ports/skia_ohos/SkFontMgr_ohos.h +@@ -0,0 +1,66 @@ ++/* ++ * Copyright 2015 Google Inc. ++ * ++ * Use of this source code is governed by a BSD-style license that can be ++ * found in the LICENSE file. ++ * 2023.4.23 SkFontMgr on ohos. ++ * Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved. ++ */ ++ ++#ifndef SKFONTMGR_OHOS_H ++#define SKFONTMGR_OHOS_H ++ ++#include "src/core/SkFontDescriptor.h" ++#include "include/core/SkFontMgr.h" ++ ++#include "FontConfig_ohos.h" ++#include "SkFontStyleSet_ohos.h" ++ ++#include "include/ports/SkFontMgr_ohos_api.h" ++ ++/*! ++ * \brief To implement the SkFontMgr for ohos platform ++ */ ++class SkFontMgr_OHOS : public SkFontMgr { ++public: ++ explicit SkFontMgr_OHOS(const char* path = nullptr); ++ virtual ~SkFontMgr_OHOS() override = default; ++ void AddSystemFont(std::string path); ++protected: ++ int onCountFamilies() const override; ++ void onGetFamilyName(int index, SkString* familyName) const override; ++ sk_sp onCreateStyleSet(int index)const override; ++ ++ sk_sp onMatchFamily(const char familyName[]) const override; ++ ++ sk_sp onMatchFamilyStyle(const char familyName[], ++ const SkFontStyle& style) const override; ++ sk_sp onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle& style, ++ const char* bcp47[], int bcp47Count, ++ SkUnichar character) const override; ++ ++ ++ sk_sp onMakeFromData(sk_sp data, int ttcIndex) const override; ++ sk_sp onMakeFromStreamIndex(std::unique_ptr stream, ++ int ttcIndex) const override; ++ sk_sp onMakeFromStreamArgs(std::unique_ptr stream, ++ const SkFontArguments& args) const override; ++ sk_sp onMakeFromFile(const char path[], int ttcIndex) const override; ++ ++ sk_sp onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override; ++ ++private: ++ std::shared_ptr fontConfig = nullptr; // the pointer of FontConfig_OHOS ++ SkFontScanner_FreeType fontScanner; // the scanner to parse a font file ++ int familyCount = 0; // the count of font style sets in generic family list ++ ++ int compareLangs(const SkString& langs, const char* bcp47[], int bcp47Count, const int tps[]) const; ++ sk_sp makeTypeface(std::unique_ptr stream, ++ const SkFontArguments& args, const char path[]) const; ++ sk_sp findTypeface(const FallbackSetPos& fallbackItem, const SkFontStyle& style, ++ const char* bcp47[], int bcp47Count, ++ SkUnichar character) const; ++}; ++ ++ ++#endif /* SKFONTMGR_OHOS_H */ +diff --git a/src/ports/skia_ohos/SkFontMgr_ohos_factory.cpp b/src/ports/skia_ohos/SkFontMgr_ohos_factory.cpp +new file mode 100644 +index 0000000000..b975ceddd4 +--- /dev/null ++++ b/src/ports/skia_ohos/SkFontMgr_ohos_factory.cpp +@@ -0,0 +1,14 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "include/core/SkFontMgr.h" ++SK_API sk_sp SkFontMgr_New_OHOS(const char* path); ++ ++/*! To implement the porting layer to return the default factory for Harmony platform ++ * \return the default font manager for Harmony platform ++ */ ++sk_sp SkFontMgr::Factory() ++{ ++ return SkFontMgr_New_OHOS(nullptr); ++} +diff --git a/src/ports/skia_ohos/SkFontStyleSet_ohos.cpp b/src/ports/skia_ohos/SkFontStyleSet_ohos.cpp +new file mode 100644 +index 0000000000..d3027858bf +--- /dev/null ++++ b/src/ports/skia_ohos/SkFontStyleSet_ohos.cpp +@@ -0,0 +1,102 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "SkFontStyleSet_ohos.h" ++ ++/*! Constructor ++ * \param fontConfig the pointer of FontConfig_OHOS ++ * \param index the index of the font style set ++ * \param isFallback true - the font style is from fallback family ++ * \n false - the font style is from generic family ++ */ ++SkFontStyleSet_OHOS::SkFontStyleSet_OHOS(const std::shared_ptr& fontConfig, ++ int index, bool isFallback) ++ : fontConfig_(fontConfig), styleIndex(index), isFallback(isFallback) ++{ ++ if (fontConfig) { ++ tpCount = fontConfig_->getTypefaceCount(styleIndex, isFallback); ++ } ++} ++ ++/*! To get the count of typeface ++ * \return The count of typeface in this font style set ++ */ ++int SkFontStyleSet_OHOS::count() ++{ ++ return tpCount; ++} ++ ++/*! To get the font style for the specified typeface ++ * \param the index of a typeface ++ * \param[out] style the style value returned to the caller ++ * \param[out] the style name returned to the caller ++ */ ++void SkFontStyleSet_OHOS::getStyle(int index, SkFontStyle* style, SkString* styleName) ++{ ++ if (index < 0 || index >= this->count() || fontConfig_ == nullptr) { ++ return; ++ } ++ ++ sk_sp typeface = fontConfig_->getTypeface(styleIndex, index, isFallback); ++ if (typeface == nullptr) { ++ return; ++ } ++ ++ if (style) { ++ *style = typeface->fontStyle(); ++ } ++ if (styleName) { ++ const char* names[] = { ++ "invisible", ++ "thin", ++ "extralight", ++ "light", ++ "normal", ++ "medium", ++ "semibold", ++ "bold", ++ "extrabold", ++ "black", ++ "extrablack" ++ }; ++ // the value of font weight is between 0 ~ 1000 (refer to SkFontStyle::Weight) ++ // the weight is divided by 100 to get the matched name ++ unsigned int i = typeface->fontStyle().weight() / 100; ++ if (i < sizeof(names) / sizeof(char*)) { ++ styleName->set(names[i]); ++ } else { ++ styleName->reset(); ++ } ++ } ++} ++ ++/*! To create a typeface ++ * \param index the index of the typeface in this font style set ++ * \return The object of a typeface, if successful ++ * \n Return null, if the 'index' is out of range ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++sk_sp SkFontStyleSet_OHOS::createTypeface(int index) ++{ ++ if (index < 0 || index >= this->count()) { ++ return nullptr; ++ } ++ if (fontConfig_) { ++ return fontConfig_->getTypeface(styleIndex, index, isFallback); ++ } ++ return nullptr; ++} ++ ++/*! To get the closest matching typeface ++ * \param pattern the style value to be matching ++ * \return the object of a typeface which is the closest matching to 'pattern' ++ * \note The caller must call unref() on the returned object ++ */ ++sk_sp SkFontStyleSet_OHOS::matchStyle(const SkFontStyle& pattern) ++{ ++ if (fontConfig_) { ++ return fontConfig_->getTypeface(styleIndex, pattern, isFallback); ++ } ++ return nullptr; ++} +diff --git a/src/ports/skia_ohos/SkFontStyleSet_ohos.h b/src/ports/skia_ohos/SkFontStyleSet_ohos.h +new file mode 100644 +index 0000000000..27a7064f86 +--- /dev/null ++++ b/src/ports/skia_ohos/SkFontStyleSet_ohos.h +@@ -0,0 +1,32 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef SKFONTSTYLESET_OHOS_H ++#define SKFONTSTYLESET_OHOS_H ++ ++#include "include/core/SkFontMgr.h" ++ ++#include "FontConfig_ohos.h" ++#include "SkTypeface_ohos.h" ++ ++/*! ++ * \brief To implement SkFontStyleSet for ohos platform ++ */ ++class SkFontStyleSet_OHOS : public SkFontStyleSet { ++public: ++ SkFontStyleSet_OHOS(const std::shared_ptr& fontConfig, ++ int index, bool isFallback = false); ++ virtual ~SkFontStyleSet_OHOS() override = default; ++ virtual int count() override; ++ virtual void getStyle(int index, SkFontStyle* style, SkString* styleName) override; ++ virtual sk_sp createTypeface(int index) override; ++ virtual sk_sp matchStyle(const SkFontStyle& pattern) override; ++private: ++ std::shared_ptr fontConfig_ = nullptr; // the object of FontConfig_OHOS ++ int styleIndex = 0; // the index of the font style set ++ bool isFallback = false; // the flag of font style set. False for fallback family, true for generic family. ++ int tpCount = -1; // the typeface count in the font style set ++}; ++ ++#endif /* SKFONTSTYLESET_OHOS_H */ +diff --git a/src/ports/skia_ohos/SkTypeface_ohos.cpp b/src/ports/skia_ohos/SkTypeface_ohos.cpp +new file mode 100644 +index 0000000000..cea92889a8 +--- /dev/null ++++ b/src/ports/skia_ohos/SkTypeface_ohos.cpp +@@ -0,0 +1,140 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "SkTypeface_ohos.h" ++ ++#include "SkDebug.h" ++#include "SkFontDescriptor.h" ++#include "SkFontHost_FreeType_common.h" ++#include "SkTArray.h" ++ ++/*! Constructor ++ * \param familyName the specified family name for the typeface ++ * \param info the font information for the typeface ++ */ ++SkTypeface_OHOS::SkTypeface_OHOS(const SkString& familyName, FontInfo& info) ++ : SkTypeface_FreeType(info.style, info.isFixedWidth), ++ specifiedName(familyName) ++{ ++ fontInfo = std::make_unique(std::move(info)); ++} ++ ++/*! Constructor ++ * \param info the font information for the typeface ++ */ ++SkTypeface_OHOS::SkTypeface_OHOS(FontInfo& info) ++ : SkTypeface_FreeType(info.style, info.isFixedWidth) ++{ ++ specifiedName.reset(); ++ fontInfo = std::make_unique(std::move(info)); ++} ++ ++/*! To get stream of the typeface ++ * \param[out] ttcIndex the index of the typeface in a ttc file returned to the caller ++ * \return The stream object of the typeface ++ */ ++std::unique_ptr SkTypeface_OHOS::onOpenStream(int* ttcIndex) const ++{ ++ if (fontInfo) { ++ if (ttcIndex) { ++ *ttcIndex = fontInfo->index; ++ } ++ if (fontInfo->stream == nullptr) { ++ fontInfo->stream = SkStream::MakeFromFile(fontInfo->fname.c_str()); ++ } ++ if (fontInfo->stream) { ++ return fontInfo->stream->duplicate(); ++ } ++ } ++ return nullptr; ++} ++ ++/*! To make font data from the typeface ++ * \return The object of SkFontData ++ */ ++std::unique_ptr SkTypeface_OHOS::onMakeFontData() const ++{ ++ if (fontInfo == nullptr) { ++ return nullptr; ++ } ++ ++ if (fontInfo->stream.get() == nullptr) { ++ fontInfo->stream = SkStream::MakeFromFile(fontInfo->fname.c_str()); ++ } ++ if (fontInfo->stream.get() == nullptr) { ++ return nullptr; ++ } ++ return std::make_unique(fontInfo->stream->duplicate(), fontInfo->index, 0, ++ fontInfo->axisSet.axis.data(), fontInfo->axisSet.axis.size(), nullptr, 0); ++} ++ ++/*! To get the font descriptor of the typeface ++ * \param[out] descriptor the font descriptor returned to the caller ++ * \param[out] isLocal the false to the caller ++ */ ++void SkTypeface_OHOS::onGetFontDescriptor(SkFontDescriptor* descriptor, bool* isLocal) const ++{ ++ if (isLocal) { ++ *isLocal = false; ++ } ++ if (descriptor) { ++ SkString familyName; ++ onGetFamilyName(&familyName); ++ descriptor->setFamilyName(familyName.c_str()); ++ descriptor->setStyle(this->fontStyle()); ++ } ++} ++ ++/*! To get the family name of the typeface ++ * \param[out] familyName the family name returned to the caller ++ */ ++void SkTypeface_OHOS::onGetFamilyName(SkString* familyName) const ++{ ++ if (familyName == nullptr) { ++ return; ++ } ++ if (specifiedName.size() > 0) { ++ *familyName = specifiedName; ++ } else { ++ if (fontInfo) { ++ *familyName = fontInfo->familyName; ++ } ++ } ++} ++ ++/*! To clone a typeface from this typeface ++ * \param args the specified font arguments from which the new typeface is created ++ * \return The object of a new typeface ++ * \note The caller must call unref() on the returned object ++ */ ++sk_sp SkTypeface_OHOS::onMakeClone(const SkFontArguments& args) const ++{ ++ FontInfo info(*(fontInfo.get())); ++ info.index = args.getCollectionIndex(); ++ unsigned int count = args.getVariationDesignPosition().coordinateCount; ++ if (count > 0 && count == fontInfo->axisSet.range.size()) { ++ SkFontArguments::VariationPosition position = args.getVariationDesignPosition(); ++ SkFontScanner::AxisDefinitions axisDefs; ++ for (unsigned int i = 0; i < count; i++) { ++ axisDefs.push_back(fontInfo->axisSet.range[i]); ++ } ++ SkFixed axisValues[count]; ++ memset(axisValues, 0, sizeof(axisValues)); ++ SkFontScanner_FreeType::computeAxisValues(axisDefs, position, ++ axisValues, fontInfo->familyName); ++ info.axisSet.axis.clear(); ++ for (unsigned int i = 0; i < count; i++) { ++ info.axisSet.axis.emplace_back(axisValues[i]); ++ } ++ } ++ return sk_make_sp(specifiedName, info); ++} ++ ++/*! To get the font information of the typeface ++ * \return The object of FontInfo ++ */ ++const FontInfo* SkTypeface_OHOS::getFontInfo() const ++{ ++ return fontInfo.get(); ++} +diff --git a/src/ports/skia_ohos/SkTypeface_ohos.h b/src/ports/skia_ohos/SkTypeface_ohos.h +new file mode 100644 +index 0000000000..d5c8a57404 +--- /dev/null ++++ b/src/ports/skia_ohos/SkTypeface_ohos.h +@@ -0,0 +1,34 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef SKTYPEFACE_OHOS_H ++#define SKTYPEFACE_OHOS_H ++ ++#include "src/ports/SkTypeface_FreeType.h" ++#include "include/core/SkFontStyle.h" ++#include "include/core/SkStream.h" ++ ++#include "FontInfo_ohos.h" ++ ++/*! ++ * \brief The implementation of SkTypeface for ohos platform ++ */ ++class SkTypeface_OHOS : public SkTypeface_FreeType { ++public: ++ SkTypeface_OHOS(const SkString& specifiedName, FontInfo& info); ++ explicit SkTypeface_OHOS(FontInfo& info); ++ virtual ~SkTypeface_OHOS() override = default; ++ const FontInfo* getFontInfo() const; ++protected: ++ std::unique_ptr onOpenStream(int* ttcIndex) const override; ++ std::unique_ptr onMakeFontData() const override; ++ void onGetFontDescriptor(SkFontDescriptor* descriptor, bool* isLocal) const override; ++ void onGetFamilyName(SkString* familyName) const override; ++ sk_sp onMakeClone(const SkFontArguments& args) const override; ++private: ++ SkString specifiedName; // specified family name which is defined in the configuration file ++ std::unique_ptr fontInfo; // the font information of this typeface ++}; ++ ++#endif /* SKTYPEFACE_OHOS_H */ +diff --git a/src/ports/skia_ohos/config/fontconfig.json b/src/ports/skia_ohos/config/fontconfig.json +new file mode 100644 +index 0000000000..ec225a188e +--- /dev/null ++++ b/src/ports/skia_ohos/config/fontconfig.json +@@ -0,0 +1,76 @@ ++{ ++ "fontdir": ["/system/fonts/"], ++ "generic": [ ++ { ++ "family": "HarmonyOS Sans", ++ "alias": [ ++ { ++ "HarmonyOS-Sans": 0 ++ }, ++ { ++ "HarmonyOS-Sans-Light": 100 ++ }, ++ { ++ "HarmonyOS-Sans-Regular": 400 ++ }, ++ { ++ "HarmonyOS-Sans-Medium": 700 ++ }, ++ { ++ "HarmonyOS-Sans-Bold": 900 ++ } ++ ], ++ "adjust": [ ++ { ++ "weight": 50, "to": 100 ++ }, ++ { ++ "weight": 80, "to": 400 ++ }, ++ { ++ "weight": 100, "to": 700 ++ }, ++ { ++ "weight": 200, "to": 900 ++ } ++ ] ++ }, ++ { ++ "family": "HarmonyOS Sans Condensed", ++ "alias": [ ++ { ++ "HarmonyOS-Sans-Condensed": 0 ++ } ++ ] ++ }, ++ { ++ "family": "HarmonyOS Sans Digit", ++ "alias": [ ++ { ++ "HarmonyOS-Sans-Digit": 0 ++ } ++ ] ++ } ++ ], ++ "fallback": [ ++ { ++ "": [ ++ { ++ "zh-Hans": "HarmonyOS Sans SC" ++ }, ++ { ++ "zh-Hant": "HarmonyOS Sans TC" ++ }, ++ { ++ "und-Arab": "HarmonyOS Sans Naskh Arabic UI" ++ }, ++ { ++ "ja": "Noto Sans JP" ++ }, ++ { ++ "ko": "Noto Sans KR" ++ } ++ ] ++ } ++ ] ++} +-- \ No newline at end of file diff --git a/attachment/repos/skia-3.22.patch1 b/attachment/repos/skia-3.22.patch1 new file mode 100644 index 0000000000000000000000000000000000000000..639c2512e34317d1805eca15e2af67f1be4df016 --- /dev/null +++ b/attachment/repos/skia-3.22.patch1 @@ -0,0 +1,107 @@ +diff --git a/src/ports/skia_ohos/FontConfig_ohos.cpp b/src/ports/skia_ohos/FontConfig_ohos.cpp +index 483083bf28..78225758d1 100644 +--- a/src/ports/skia_ohos/FontConfig_ohos.cpp ++++ b/src/ports/skia_ohos/FontConfig_ohos.cpp +@@ -1323,7 +1323,11 @@ int FontConfig_OHOS::checkProductFile(const char* fname) + return err; + } + +-void FontConfig_OHOS::setSystemDefaultFont(sk_sp typeface) { ++void FontConfig_OHOS::resetSystemDefaultFont() { ++ genericFamilySet.erase(genericFamilySet.begin()); ++} ++ ++void FontConfig_OHOS::addCustomFont(int familyCount, sk_sp typeface) { + std::unique_ptr familySet = std::make_unique(); + typeface->getFamilyName(&(familySet->familyName)); + familySet->typefaceSet = std::make_shared(); +@@ -1331,6 +1335,10 @@ void FontConfig_OHOS::setSystemDefaultFont(sk_sp typeface) { + SkTypeface_OHOS* typeface_ = (SkTypeface_OHOS* )(typeface.get()); + FontInfo info = FontInfo(*(typeface_->getFontInfo())); + familySet->typefaceSet->push_back(sk_make_sp(info)); +- +- genericFamilySet.insert(genericFamilySet.begin(), std::move(familySet)); ++ ++ if (familyCount == this->getFamilyCount()) { ++ genericFamilySet.insert(genericFamilySet.begin(), std::move(familySet)); ++ } else { ++ genericFamilySet[0] = std::move(familySet); ++ } + } +diff --git a/src/ports/skia_ohos/FontConfig_ohos.h b/src/ports/skia_ohos/FontConfig_ohos.h +index d53ce299cf..9311f237bc 100644 +--- a/src/ports/skia_ohos/FontConfig_ohos.h ++++ b/src/ports/skia_ohos/FontConfig_ohos.h +@@ -110,7 +110,8 @@ public: + static sk_sp matchFontStyle(const TypefaceSet& typefaceSet, const SkFontStyle& pattern); + + static const char* errToString(int err); +- void setSystemDefaultFont(sk_sp typeface); ++ void resetSystemDefaultFont(); ++ void addCustomFont(int familyCount, sk_sp typeface); + private: + struct AliasInfo; + struct AdjustInfo; +diff --git a/src/ports/skia_ohos/SkFontMgr_ohos.cpp b/src/ports/skia_ohos/SkFontMgr_ohos.cpp +index b36c1fe2d0..c1477a1f1b 100644 +--- a/src/ports/skia_ohos/SkFontMgr_ohos.cpp ++++ b/src/ports/skia_ohos/SkFontMgr_ohos.cpp +@@ -2,6 +2,8 @@ + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include ++ + #include "SkFontMgr_ohos.h" + + #include "SkTypeface_ohos.h" +@@ -418,17 +420,27 @@ sk_sp SkFontMgr_OHOS::makeTypeface(std::unique_ptr st + return sk_make_sp(fontInfo); + } + +-void SkFontMgr_OHOS::AddSystemFont(std::string path) { ++void SkFontMgr_OHOS::InitializeSystemFont(std::string path) { + static SkOnce once; +- + once([this, path]{ ++ this->AddSystemFont(path); ++ }); ++} ++ ++void SkFontMgr_OHOS::AddSystemFont(std::string path) { ++ std::string fileName = std::filesystem::path(path).filename(); ++ if (fileName == OHOS_DEFAULT_FONT_FILENAME) { ++ if (familyCount != this->fontConfig->getFamilyCount()) { ++ this->fontConfig->resetSystemDefaultFont(); ++ } ++ } else { + sk_sp typeface = this->onMakeFromFile(path.c_str(), 0); + if(!typeface) { + SkDEBUGF("parse system font failed"); + return; + } +- this->fontConfig->setSystemDefaultFont(typeface); +- }); ++ this->fontConfig->addCustomFont(familyCount, typeface); ++ } + } + + /*! To create SkFontMgr object for Harmony platform +diff --git a/src/ports/skia_ohos/SkFontMgr_ohos.h b/src/ports/skia_ohos/SkFontMgr_ohos.h +index c0a8379174..1ce8c79eb5 100644 +--- a/src/ports/skia_ohos/SkFontMgr_ohos.h ++++ b/src/ports/skia_ohos/SkFontMgr_ohos.h +@@ -21,10 +21,12 @@ + /*! + * \brief To implement the SkFontMgr for ohos platform + */ ++#define OHOS_DEFAULT_FONT_FILENAME "default.ttf" + class SkFontMgr_OHOS : public SkFontMgr { + public: + explicit SkFontMgr_OHOS(const char* path = nullptr); + virtual ~SkFontMgr_OHOS() override = default; ++ void InitializeSystemFont(std::string path); + void AddSystemFont(std::string path); + protected: + int onCountFamilies() const override; +-- \ No newline at end of file diff --git a/attachment/repos/skia-3.22.patch2 b/attachment/repos/skia-3.22.patch2 new file mode 100644 index 0000000000000000000000000000000000000000..4683ca466ae8f00df7b74af2f1b9175c2d908599 --- /dev/null +++ b/attachment/repos/skia-3.22.patch2 @@ -0,0 +1,14 @@ +diff --git a/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp b/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp +index 17615797a1..5d9f7229d2 100644 +--- a/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp ++++ b/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp +@@ -1953,6 +1953,8 @@ void GLSLCodeGenerator::writeInputVars() { + + bool GLSLCodeGenerator::generateCode() { + this->writeHeader(); ++ // Tell driver this shader generated by skia ++ this->writeLine("// GL_EXTENSION_SKIA_CACHE"); + OutputStream* rawOut = fOut; + StringStream body; + fOut = &body; +-- \ No newline at end of file diff --git a/attachment/repos/skia-3.22.patch3 b/attachment/repos/skia-3.22.patch3 new file mode 100644 index 0000000000000000000000000000000000000000..85527258f303430fa65d6b73460a85d9291fc56b --- /dev/null +++ b/attachment/repos/skia-3.22.patch3 @@ -0,0 +1,51 @@ +diff --git a/src/ports/skia_ohos/FontConfig_ohos.cpp b/src/ports/skia_ohos/FontConfig_ohos.cpp +index 78225758d1..405bcb3c14 100644 +--- a/src/ports/skia_ohos/FontConfig_ohos.cpp ++++ b/src/ports/skia_ohos/FontConfig_ohos.cpp +@@ -520,7 +520,7 @@ int FontConfig_OHOS::parseGeneric(const Json::Value& root) + return logErrInfo(ERROR_CONFIG_MISSING_TAG, "alias"); + } + // "adjust", "variation" - optional +- const char* tags[] = {"alias", "adjust", "variations", "index"}; ++ const char* tags[] = {"alias", "adjust", "font-variations", "index"}; + std::vector aliasSet; + std::vector adjustSet; + std::vector variationSet; +@@ -685,7 +685,7 @@ int FontConfig_OHOS::parseFallbackItem(const Json::Value& root) + bool hasIndex = false; + bool hasVariations = false; + for (unsigned int i = 0; i < members.size(); i++) { +- if (members[i] == "variations") { ++ if (members[i] == "font-variations") { + hasVariations = true; + } else if (members[i] == "index") { + hasIndex = true; +@@ -703,7 +703,7 @@ int FontConfig_OHOS::parseFallbackItem(const Json::Value& root) + SkString lang = SkString(key); + SkString familyName = SkString(root[key].asCString()); + if (hasVariations) { +- key = "variations"; ++ key = "font-variations"; + if (root[key].isArray()) { + const Json::Value& varArr = root[key]; + std::vector variationSet; +@@ -711,7 +711,7 @@ int FontConfig_OHOS::parseFallbackItem(const Json::Value& root) + if (varArr[i].isObject()) { + parseVariation(varArr[i], variationSet); + } else { +- SkString text = SkString("variations#"); ++ SkString text = SkString("font-variations#"); + text.appendU32(i + 1); + (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, text.c_str(), + Json::objectValue, varArr[i].type()); +@@ -742,8 +742,8 @@ int FontConfig_OHOS::parseFallbackItem(const Json::Value& root) + return NO_ERROR; + } + +-/*! To parse an item of 'variations' attribute +- * \param root the root node of an item in 'variations' list ++/*! To parse an item of 'font-variations' attribute ++ * \param root the root node of an item in 'font-variations' list + * \param[out] variationSet the value of VariationInfo is written to and returned to the caller + * \return NO_ERROR successful + * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute diff --git a/attachment/repos/skia.patch b/attachment/repos/skia.patch new file mode 100644 index 0000000000000000000000000000000000000000..d993bdffa7bae8ef54f07c45fbb2824284ef191c --- /dev/null +++ b/attachment/repos/skia.patch @@ -0,0 +1,3728 @@ +diff --git a/BUILD.gn b/BUILD.gn +index ebbd898ec9..2d4257e8d7 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -26,16 +26,21 @@ import("gn/ios.gni") + + # Skia public API, generally provided by :skia. + config("skia_public") { +- include_dirs = [ "." ] ++ include_dirs = [ ".", ++ "//third_part/khronos/", ] + + defines = [] + cflags_objcc = [] + if (is_component_build) { + defines += [ "SKIA_DLL" ] + } +- if (is_fuchsia || is_linux) { ++ if (is_fuchsia || is_linux || is_ohos) { + defines += [ "SK_R32_SHIFT=16" ] + } ++ ++ if( is_ohos && !is_win ) { ++ defines += [ "TARGET_OS_OHOS" ] ++ } + if (skia_enable_flutter_defines) { + defines += flutter_defines + } +@@ -48,7 +53,7 @@ config("skia_public") { + "SK_DISABLE_AAA", + ] + } +- if (skia_enable_sksl) { ++ if (skia_enable_sksl && !is_win) { + defines += [ "SK_ENABLE_SKSL" ] + } + if (skia_enable_precompile) { +@@ -417,6 +422,44 @@ optional("fontmgr_android_factory") { + sources = [ "src/ports/SkFontMgr_android_factory.cpp" ] + } + ++optional("fontmgr_ohos") { ++ enabled = skia_enable_fontmgr_ohos ++ deps = [ ++ ":typeface_freetype", ++ "//third_party/expat", ++ "//third_party/jsoncpp:jsoncpp", ++ ] ++ public = [ ++ "src/ports/skia_ohos/FontConfig_ohos.h", ++ "src/ports/skia_ohos/FontInfo_ohos.h", ++ "src/ports/skia_ohos/SkFontMgr_ohos.h", ++ "src/ports/skia_ohos/SkFontStyleSet_ohos.h", ++ "src/ports/skia_ohos/SkTypeface_ohos.h", ++ ] ++ sources = [ ++ "src/ports/skia_ohos/FontConfig_ohos.cpp", ++ "src/ports/skia_ohos/SkFontMgr_ohos.cpp", ++ "src/ports/skia_ohos/SkFontStyleSet_ohos.cpp", ++ "src/ports/skia_ohos/SkTypeface_ohos.cpp", ++ "//third_party/vulkan-deps/spirv-headers/src/tools/buildHeaders/jsoncpp/dist/jsoncpp.cpp", ++ ] ++ include_dirs = [ ++ "//third_party/skia/include/private", ++ "//third_party/skia/include/core", ++ "//third_party/skia/src/core", ++ "//third_party/skia/src/ports", ++ "//third_party/skia/src/ports/skia_ohos", ++ "//third_party/vulkan-deps/spirv-headers/src/tools/buildHeaders/jsoncpp/dist/", ++ "//third_party/skia/src/core/SkFontDescriptor.h" ++ ] ++} ++ ++optional("fontmgr_ohos_factory") { ++ enabled = skia_enable_fontmgr_ohos ++ deps = [ ":fontmgr_ohos" ] ++ sources = [ "src/ports/skia_ohos/SkFontMgr_ohos_factory.cpp" ] ++} ++ + optional("fontmgr_custom") { + enabled = + skia_enable_fontmgr_custom_directory || +@@ -690,7 +733,7 @@ if (skia_compile_modules) { + sources += [ "tools/SkGetExecutablePath_win.cpp" ] + } else if (is_mac || is_ios) { + sources += [ "tools/SkGetExecutablePath_mac.cpp" ] +- } else if (is_linux || is_android) { ++ } else if (is_linux || is_android || is_ohos) { + sources += [ "tools/SkGetExecutablePath_linux.cpp" ] + } + if (is_win) { +@@ -818,7 +861,7 @@ if (skia_compile_sksl_tests) { + sources += [ "tools/SkGetExecutablePath_win.cpp" ] + } else if (is_mac || is_ios) { + sources += [ "tools/SkGetExecutablePath_mac.cpp" ] +- } else if (is_linux || is_android) { ++ } else if (is_linux || is_android || is_ohos) { + sources += [ "tools/SkGetExecutablePath_linux.cpp" ] + } + if (is_win) { +@@ -1077,7 +1120,7 @@ optional("gpu") { + libs += [ "EGL" ] + } else if (skia_use_webgl) { + sources += [ "src/gpu/ganesh/gl/webgl/GrGLMakeNativeInterface_webgl.cpp" ] +- } else if (is_linux && skia_use_x11) { ++ } else if ((is_linux || is_ohos) && skia_use_x11) { + sources += [ + "src/gpu/ganesh/gl/glx/GrGLMakeGLXInterface.cpp", + "src/gpu/ganesh/gl/glx/GrGLMakeNativeInterface_glx.cpp", +@@ -1092,8 +1135,16 @@ optional("gpu") { + if (target_cpu != "arm64") { + libs += [ "OpenGL32.lib" ] + } +- } else { +- sources += [ "src/gpu/ganesh/gl/GrGLMakeNativeInterface_none.cpp" ] ++ } else if( is_ohos){ ++ libs += [ "EGL" ,"GLESv3"] ++ sources += [ ++ "src/gpu/ganesh/gl/egl/GrGLMakeEGLInterface.cpp", ++ "src/gpu/ganesh/gl/egl/GrGLMakeNativeInterface_egl.cpp", ++ ] ++ }else { ++ sources += [ ++ "src/gpu/ganesh/gl/GrGLMakeNativeInterface_none.cpp", ++ ] + } + sources += skia_gl_gpu_sources + } +@@ -1426,6 +1477,7 @@ skia_component("skia") { + ":fontmgr_fuchsia", + ":fontmgr_mac_ct", + ":fontmgr_win", ++ ":fontmgr_ohos", + ":fontmgr_win_gdi", + ":gpu", + ":graphite", +@@ -1529,6 +1581,7 @@ skia_component("skia") { + libs += [ + "Ole32.lib", + "OleAut32.lib", ++ "OpenGL32.lib", + ] + + if (!skia_enable_winuwp) { +@@ -1559,7 +1612,7 @@ skia_component("skia") { + ] + } + +- if (is_linux || is_wasm) { ++ if (is_linux || is_wasm || is_ohos) { + sources += [ "src/ports/SkDebug_stdio.cpp" ] + if (skia_use_egl) { + libs += [ "GLESv2" ] +@@ -1962,7 +2015,7 @@ if (skia_enable_tools) { + if (is_android || skia_use_egl) { + sources += [ "tools/gpu/gl/egl/CreatePlatformGLTestContext_egl.cpp" ] + libs += [ "EGL" ] +- } else if (is_linux) { ++ } else if (is_linux || is_ohos) { + sources += [ "tools/gpu/gl/glx/CreatePlatformGLTestContext_glx.cpp" ] + libs += [ + "GLU", +@@ -2281,7 +2334,7 @@ if (skia_enable_tools) { + ] + } + +- if (is_linux || is_mac || skia_enable_optimize_size) { ++ if (is_linux || is_mac || skia_enable_optimize_size || is_ohos) { + if (skia_enable_skottie) { + test_app("skottie_tool") { + deps = [ "modules/skottie:tool" ] +@@ -2777,7 +2830,7 @@ if (skia_enable_tools) { + "tools/sk_app/android/surface_glue_android.h", + ] + libs += [ "android" ] +- } else if (is_linux) { ++ } else if (is_linux || is_ohos) { + sources += [ + "tools/SkGetExecutablePath_linux.cpp", + "tools/sk_app/unix/RasterWindowContext_unix.cpp", +@@ -2830,7 +2883,7 @@ if (skia_enable_tools) { + sources += [ "tools/sk_app/GLWindowContext.h" ] + if (is_android) { + sources += [ "tools/sk_app/android/GLWindowContext_android.cpp" ] +- } else if (is_linux) { ++ } else if (is_linux || is_ohos) { + sources += [ "tools/sk_app/unix/GLWindowContext_unix.cpp" ] + } else if (is_win) { + sources += [ "tools/sk_app/win/GLWindowContext_win.cpp" ] +@@ -2855,7 +2908,7 @@ if (skia_enable_tools) { + sources += [ "tools/sk_app/VulkanWindowContext.h" ] + if (is_android) { + sources += [ "tools/sk_app/android/VulkanWindowContext_android.cpp" ] +- } else if (is_linux) { ++ } else if (is_linux || is_ohos) { + sources += [ "tools/sk_app/unix/VulkanWindowContext_unix.cpp" ] + libs += [ "X11-xcb" ] + } else if (is_win) { +@@ -2887,7 +2940,7 @@ if (skia_enable_tools) { + if (skia_use_dawn) { + sources += [ "tools/sk_app/DawnWindowContext.cpp" ] + sources += [ "tools/sk_app/DawnWindowContext.h" ] +- if (is_linux) { ++ if (is_linux || is_ohos) { + if (dawn_enable_vulkan) { + sources += [ "tools/sk_app/unix/DawnVulkanWindowContext_unix.cpp" ] + defines = [ "VK_USE_PLATFORM_XCB_KHR" ] +@@ -2931,7 +2984,7 @@ if (skia_enable_tools) { + } + } + +- if (!skia_use_vulkan && (is_mac || is_linux || is_win)) { ++ if (!skia_use_vulkan && (is_mac || is_linux || is_win || is_ohos)) { + test_app("fiddle_examples") { + sources = [ + "tools/fiddle/all_examples.cpp", +@@ -3088,7 +3141,7 @@ if (skia_enable_tools) { + } + } + +- if (skia_use_gl && !skia_use_angle && (is_linux || is_win || is_mac)) { ++ if (skia_use_gl && !skia_use_angle && (is_linux || is_win || is_mac || is_ohos)) { + test_app("HelloWorld") { + sources = [ "example/HelloWorld.cpp" ] + libs = [] +diff --git a/gn/BUILDCONFIG.gn b/gn/BUILDCONFIG.gn +index 44f39c5aaa..c4eac1eb66 100644 +--- a/gn/BUILDCONFIG.gn ++++ b/gn/BUILDCONFIG.gn +@@ -63,6 +63,7 @@ is_linux = current_os == "linux" + is_mac = current_os == "mac" + is_wasm = current_os == "wasm" + is_win = current_os == "win" ++is_ohos = current_os == "ohos" + + # This is just to make the Dawn build files happy. Skia itself uses target_os = "linux" + # for ChromeOS, so this variable will not affect Skia proper. +diff --git a/gn/skia.gni b/gn/skia.gni +index b81c0dae54..deb6451271 100644 +--- a/gn/skia.gni ++++ b/gn/skia.gni +@@ -20,6 +20,7 @@ declare_args() { + skia_enable_fontmgr_empty = false + skia_enable_fontmgr_fuchsia = is_fuchsia + skia_enable_fontmgr_win = is_win ++ skia_enable_fontmgr_ohos = is_ohos + skia_enable_gpu = true + skia_enable_optimize_size = false + skia_enable_pdf = !is_wasm +@@ -46,9 +47,9 @@ declare_args() { + skia_use_expat = !is_wasm + skia_use_ffmpeg = false + skia_use_fixed_gamma_text = is_android +- skia_use_fontconfig = is_linux ++ skia_use_fontconfig = is_linux || is_ohos + skia_use_fonthost_mac = is_mac || is_ios +- skia_use_freetype = is_android || is_fuchsia || is_linux || is_wasm ++ skia_use_freetype = is_android || is_fuchsia || is_linux || is_wasm || is_ohos + skia_use_harfbuzz = true + skia_use_gl = !is_fuchsia + skia_use_icu = !is_fuchsia +@@ -64,14 +65,14 @@ declare_args() { + skia_use_lua = is_skia_dev_build && !is_ios + skia_use_metal = false + skia_use_ndk_images = is_android && defined(ndk_api) && ndk_api >= 30 +- skia_use_perfetto = is_linux || is_mac || is_android ++ skia_use_perfetto = is_linux || is_mac || is_android || is_ohos + skia_use_piet = false + skia_use_piex = !is_win && !is_wasm + skia_use_sfml = false + skia_use_webgl = is_wasm + skia_use_webgpu = is_wasm + skia_use_wuffs = true +- skia_use_x11 = is_linux ++ skia_use_x11 = is_linux || is_ohos + skia_use_xps = true + skia_enable_graphite = false + skia_use_zlib = true +@@ -119,6 +120,8 @@ declare_args() { + } + + declare_args() { ++# skia_enable_fontmgr_ohos = is_ohos ++ skia_use_freetype2 = true + skia_enable_fontmgr_android = skia_use_expat && skia_use_freetype + skia_enable_fontmgr_custom_directory = + skia_use_freetype && !is_fuchsia && !is_wasm +@@ -143,7 +146,9 @@ declare_args() { + + declare_args() { + # skia_fontmgr_factory should define SkFontMgr::Factory() +- if (skia_enable_fontmgr_empty) { ++ if (is_ohos) { ++ skia_fontmgr_factory = ":fontmgr_ohos_factory" ++ } else if (skia_enable_fontmgr_empty) { + skia_fontmgr_factory = ":fontmgr_empty_factory" + } else if (is_android && skia_enable_fontmgr_android) { + skia_fontmgr_factory = ":fontmgr_android_factory" +diff --git a/gn/skia/BUILD.gn b/gn/skia/BUILD.gn +index b43526fe1f..fb34289da7 100644 +--- a/gn/skia/BUILD.gn ++++ b/gn/skia/BUILD.gn +@@ -235,7 +235,7 @@ config("default") { + } + } + +- if (is_linux) { ++ if (is_linux || is_ohos) { + libs += [ "pthread" ] + } + +@@ -333,7 +333,7 @@ config("default") { + ldflags += [ "-fsanitize=$sanitizers" ] + } + +- if (is_linux) { ++ if (is_linux || is_ohos) { + cflags_cc += [ "-stdlib=libc++" ] + ldflags += [ "-stdlib=libc++" ] + } +@@ -685,7 +685,7 @@ config("executable") { + ] + } else if (is_mac) { + ldflags = [ "-Wl,-rpath,@loader_path/." ] +- } else if (is_linux) { ++ } else if (is_linux || is_ohos) { + ldflags = [ + "-rdynamic", + "-Wl,-rpath,\$ORIGIN", +diff --git a/src/gpu/ganesh/gl/egl/GrGLMakeEGLInterface.cpp b/src/gpu/ganesh/gl/egl/GrGLMakeEGLInterface.cpp +index 78225b4610..c2105852b1 100644 +--- a/src/gpu/ganesh/gl/egl/GrGLMakeEGLInterface.cpp ++++ b/src/gpu/ganesh/gl/egl/GrGLMakeEGLInterface.cpp +@@ -12,7 +12,9 @@ + #ifndef GL_GLEXT_PROTOTYPES + #define GL_GLEXT_PROTOTYPES + #endif +-#include ++// #include ++#include "gl2.h" ++ + + static GrGLFuncPtr egl_get_gl_proc(void* ctx, const char name[]) { + SkASSERT(nullptr == ctx); +diff --git a/src/gpu/ganesh/gl/egl/gl2.h b/src/gpu/ganesh/gl/egl/gl2.h +new file mode 100644 +index 0000000000..176023660e +--- /dev/null ++++ b/src/gpu/ganesh/gl/egl/gl2.h +@@ -0,0 +1,678 @@ ++#ifndef __gles2_gl2_h_ ++#define __gles2_gl2_h_ 1 ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* ++** Copyright (c) 2013-2018 The Khronos Group Inc. ++** ++** Permission is hereby granted, free of charge, to any person obtaining a ++** copy of this software and/or associated documentation files (the ++** "Materials"), to deal in the Materials without restriction, including ++** without limitation the rights to use, copy, modify, merge, publish, ++** distribute, sublicense, and/or sell copies of the Materials, and to ++** permit persons to whom the Materials are furnished to do so, subject to ++** the following conditions: ++** ++** The above copyright notice and this permission notice shall be included ++** in all copies or substantial portions of the Materials. ++** ++** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ++** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ++** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ++** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ++** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. ++*/ ++/* ++** This header is generated from the Khronos OpenGL / OpenGL ES XML ++** API Registry. The current version of the Registry, generator scripts ++** used to make the header, and the header can be found at ++** https://github.com/KhronosGroup/OpenGL-Registry ++*/ ++ ++/* Chromium-specific GLES2 declarations */ ++// #include ++ ++#include "gl2platform.h" ++ ++#ifndef GL_APIENTRYP ++#define GL_APIENTRYP GL_APIENTRY* ++#endif ++ ++#ifndef GL_GLES_PROTOTYPES ++#define GL_GLES_PROTOTYPES 1 ++#endif ++ ++/* Generated on date 20190911 */ ++ ++/* Generated C header for: ++ * API: gles2 ++ * Profile: common ++ * Versions considered: 2\.[0-9] ++ * Versions emitted: .* ++ * Default extensions included: None ++ * Additional extensions included: _nomatch_^ ++ * Extensions removed: _nomatch_^ ++ */ ++ ++#ifndef GL_ES_VERSION_2_0 ++#define GL_ES_VERSION_2_0 1 ++#include ++typedef khronos_int8_t GLbyte; ++typedef khronos_float_t GLclampf; ++typedef khronos_int32_t GLfixed; ++typedef khronos_int16_t GLshort; ++typedef khronos_uint16_t GLushort; ++typedef void GLvoid; ++typedef struct __GLsync *GLsync; ++typedef khronos_int64_t GLint64; ++typedef khronos_uint64_t GLuint64; ++typedef unsigned int GLenum; ++typedef unsigned int GLuint; ++typedef char GLchar; ++typedef khronos_float_t GLfloat; ++typedef khronos_ssize_t GLsizeiptr; ++typedef khronos_intptr_t GLintptr; ++typedef unsigned int GLbitfield; ++typedef int GLint; ++typedef unsigned char GLboolean; ++typedef int GLsizei; ++typedef khronos_uint8_t GLubyte; ++#define GL_DEPTH_BUFFER_BIT 0x00000100 ++#define GL_STENCIL_BUFFER_BIT 0x00000400 ++#define GL_COLOR_BUFFER_BIT 0x00004000 ++#define GL_FALSE 0 ++#define GL_TRUE 1 ++#define GL_POINTS 0x0000 ++#define GL_LINES 0x0001 ++#define GL_LINE_LOOP 0x0002 ++#define GL_LINE_STRIP 0x0003 ++#define GL_TRIANGLES 0x0004 ++#define GL_TRIANGLE_STRIP 0x0005 ++#define GL_TRIANGLE_FAN 0x0006 ++#define GL_ZERO 0 ++#define GL_ONE 1 ++#define GL_SRC_COLOR 0x0300 ++#define GL_ONE_MINUS_SRC_COLOR 0x0301 ++#define GL_SRC_ALPHA 0x0302 ++#define GL_ONE_MINUS_SRC_ALPHA 0x0303 ++#define GL_DST_ALPHA 0x0304 ++#define GL_ONE_MINUS_DST_ALPHA 0x0305 ++#define GL_DST_COLOR 0x0306 ++#define GL_ONE_MINUS_DST_COLOR 0x0307 ++#define GL_SRC_ALPHA_SATURATE 0x0308 ++#define GL_FUNC_ADD 0x8006 ++#define GL_BLEND_EQUATION 0x8009 ++#define GL_BLEND_EQUATION_RGB 0x8009 ++#define GL_BLEND_EQUATION_ALPHA 0x883D ++#define GL_FUNC_SUBTRACT 0x800A ++#define GL_FUNC_REVERSE_SUBTRACT 0x800B ++#define GL_BLEND_DST_RGB 0x80C8 ++#define GL_BLEND_SRC_RGB 0x80C9 ++#define GL_BLEND_DST_ALPHA 0x80CA ++#define GL_BLEND_SRC_ALPHA 0x80CB ++#define GL_CONSTANT_COLOR 0x8001 ++#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 ++#define GL_CONSTANT_ALPHA 0x8003 ++#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 ++#define GL_BLEND_COLOR 0x8005 ++#define GL_ARRAY_BUFFER 0x8892 ++#define GL_ELEMENT_ARRAY_BUFFER 0x8893 ++#define GL_ARRAY_BUFFER_BINDING 0x8894 ++#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 ++#define GL_STREAM_DRAW 0x88E0 ++#define GL_STATIC_DRAW 0x88E4 ++#define GL_DYNAMIC_DRAW 0x88E8 ++#define GL_BUFFER_SIZE 0x8764 ++#define GL_BUFFER_USAGE 0x8765 ++#define GL_CURRENT_VERTEX_ATTRIB 0x8626 ++#define GL_FRONT 0x0404 ++#define GL_BACK 0x0405 ++#define GL_FRONT_AND_BACK 0x0408 ++#define GL_TEXTURE_2D 0x0DE1 ++#define GL_CULL_FACE 0x0B44 ++#define GL_BLEND 0x0BE2 ++#define GL_DITHER 0x0BD0 ++#define GL_STENCIL_TEST 0x0B90 ++#define GL_DEPTH_TEST 0x0B71 ++#define GL_SCISSOR_TEST 0x0C11 ++#define GL_POLYGON_OFFSET_FILL 0x8037 ++#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E ++#define GL_SAMPLE_COVERAGE 0x80A0 ++#define GL_NO_ERROR 0 ++#define GL_INVALID_ENUM 0x0500 ++#define GL_INVALID_VALUE 0x0501 ++#define GL_INVALID_OPERATION 0x0502 ++#define GL_OUT_OF_MEMORY 0x0505 ++#define GL_CW 0x0900 ++#define GL_CCW 0x0901 ++#define GL_LINE_WIDTH 0x0B21 ++#define GL_ALIASED_POINT_SIZE_RANGE 0x846D ++#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E ++#define GL_CULL_FACE_MODE 0x0B45 ++#define GL_FRONT_FACE 0x0B46 ++#define GL_DEPTH_RANGE 0x0B70 ++#define GL_DEPTH_WRITEMASK 0x0B72 ++#define GL_DEPTH_CLEAR_VALUE 0x0B73 ++#define GL_DEPTH_FUNC 0x0B74 ++#define GL_STENCIL_CLEAR_VALUE 0x0B91 ++#define GL_STENCIL_FUNC 0x0B92 ++#define GL_STENCIL_FAIL 0x0B94 ++#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 ++#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 ++#define GL_STENCIL_REF 0x0B97 ++#define GL_STENCIL_VALUE_MASK 0x0B93 ++#define GL_STENCIL_WRITEMASK 0x0B98 ++#define GL_STENCIL_BACK_FUNC 0x8800 ++#define GL_STENCIL_BACK_FAIL 0x8801 ++#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 ++#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 ++#define GL_STENCIL_BACK_REF 0x8CA3 ++#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 ++#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 ++#define GL_VIEWPORT 0x0BA2 ++#define GL_SCISSOR_BOX 0x0C10 ++#define GL_COLOR_CLEAR_VALUE 0x0C22 ++#define GL_COLOR_WRITEMASK 0x0C23 ++#define GL_UNPACK_ALIGNMENT 0x0CF5 ++#define GL_PACK_ALIGNMENT 0x0D05 ++#define GL_MAX_TEXTURE_SIZE 0x0D33 ++#define GL_MAX_VIEWPORT_DIMS 0x0D3A ++#define GL_SUBPIXEL_BITS 0x0D50 ++#define GL_RED_BITS 0x0D52 ++#define GL_GREEN_BITS 0x0D53 ++#define GL_BLUE_BITS 0x0D54 ++#define GL_ALPHA_BITS 0x0D55 ++#define GL_DEPTH_BITS 0x0D56 ++#define GL_STENCIL_BITS 0x0D57 ++#define GL_POLYGON_OFFSET_UNITS 0x2A00 ++#define GL_POLYGON_OFFSET_FACTOR 0x8038 ++#define GL_TEXTURE_BINDING_2D 0x8069 ++#define GL_SAMPLE_BUFFERS 0x80A8 ++#define GL_SAMPLES 0x80A9 ++#define GL_SAMPLE_COVERAGE_VALUE 0x80AA ++#define GL_SAMPLE_COVERAGE_INVERT 0x80AB ++#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 ++#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 ++#define GL_DONT_CARE 0x1100 ++#define GL_FASTEST 0x1101 ++#define GL_NICEST 0x1102 ++#define GL_GENERATE_MIPMAP_HINT 0x8192 ++#define GL_BYTE 0x1400 ++#define GL_UNSIGNED_BYTE 0x1401 ++#define GL_SHORT 0x1402 ++#define GL_UNSIGNED_SHORT 0x1403 ++#define GL_INT 0x1404 ++#define GL_UNSIGNED_INT 0x1405 ++#define GL_FLOAT 0x1406 ++#define GL_FIXED 0x140C ++#define GL_DEPTH_COMPONENT 0x1902 ++#define GL_ALPHA 0x1906 ++#define GL_RGB 0x1907 ++#define GL_RGBA 0x1908 ++#define GL_LUMINANCE 0x1909 ++#define GL_LUMINANCE_ALPHA 0x190A ++#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 ++#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 ++#define GL_UNSIGNED_SHORT_5_6_5 0x8363 ++#define GL_FRAGMENT_SHADER 0x8B30 ++#define GL_VERTEX_SHADER 0x8B31 ++#define GL_MAX_VERTEX_ATTRIBS 0x8869 ++#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB ++#define GL_MAX_VARYING_VECTORS 0x8DFC ++#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D ++#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C ++#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 ++#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD ++#define GL_SHADER_TYPE 0x8B4F ++#define GL_DELETE_STATUS 0x8B80 ++#define GL_LINK_STATUS 0x8B82 ++#define GL_VALIDATE_STATUS 0x8B83 ++#define GL_ATTACHED_SHADERS 0x8B85 ++#define GL_ACTIVE_UNIFORMS 0x8B86 ++#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 ++#define GL_ACTIVE_ATTRIBUTES 0x8B89 ++#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A ++#define GL_SHADING_LANGUAGE_VERSION 0x8B8C ++#define GL_CURRENT_PROGRAM 0x8B8D ++#define GL_NEVER 0x0200 ++#define GL_LESS 0x0201 ++#define GL_EQUAL 0x0202 ++#define GL_LEQUAL 0x0203 ++#define GL_GREATER 0x0204 ++#define GL_NOTEQUAL 0x0205 ++#define GL_GEQUAL 0x0206 ++#define GL_ALWAYS 0x0207 ++#define GL_KEEP 0x1E00 ++#define GL_REPLACE 0x1E01 ++#define GL_INCR 0x1E02 ++#define GL_DECR 0x1E03 ++#define GL_INVERT 0x150A ++#define GL_INCR_WRAP 0x8507 ++#define GL_DECR_WRAP 0x8508 ++#define GL_VENDOR 0x1F00 ++#define GL_RENDERER 0x1F01 ++#define GL_VERSION 0x1F02 ++#define GL_EXTENSIONS 0x1F03 ++#define GL_NEAREST 0x2600 ++#define GL_LINEAR 0x2601 ++#define GL_NEAREST_MIPMAP_NEAREST 0x2700 ++#define GL_LINEAR_MIPMAP_NEAREST 0x2701 ++#define GL_NEAREST_MIPMAP_LINEAR 0x2702 ++#define GL_LINEAR_MIPMAP_LINEAR 0x2703 ++#define GL_TEXTURE_MAG_FILTER 0x2800 ++#define GL_TEXTURE_MIN_FILTER 0x2801 ++#define GL_TEXTURE_WRAP_S 0x2802 ++#define GL_TEXTURE_WRAP_T 0x2803 ++#define GL_TEXTURE 0x1702 ++#define GL_TEXTURE_CUBE_MAP 0x8513 ++#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 ++#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 ++#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 ++#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 ++#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 ++#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 ++#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A ++#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C ++#define GL_TEXTURE0 0x84C0 ++#define GL_TEXTURE1 0x84C1 ++#define GL_TEXTURE2 0x84C2 ++#define GL_TEXTURE3 0x84C3 ++#define GL_TEXTURE4 0x84C4 ++#define GL_TEXTURE5 0x84C5 ++#define GL_TEXTURE6 0x84C6 ++#define GL_TEXTURE7 0x84C7 ++#define GL_TEXTURE8 0x84C8 ++#define GL_TEXTURE9 0x84C9 ++#define GL_TEXTURE10 0x84CA ++#define GL_TEXTURE11 0x84CB ++#define GL_TEXTURE12 0x84CC ++#define GL_TEXTURE13 0x84CD ++#define GL_TEXTURE14 0x84CE ++#define GL_TEXTURE15 0x84CF ++#define GL_TEXTURE16 0x84D0 ++#define GL_TEXTURE17 0x84D1 ++#define GL_TEXTURE18 0x84D2 ++#define GL_TEXTURE19 0x84D3 ++#define GL_TEXTURE20 0x84D4 ++#define GL_TEXTURE21 0x84D5 ++#define GL_TEXTURE22 0x84D6 ++#define GL_TEXTURE23 0x84D7 ++#define GL_TEXTURE24 0x84D8 ++#define GL_TEXTURE25 0x84D9 ++#define GL_TEXTURE26 0x84DA ++#define GL_TEXTURE27 0x84DB ++#define GL_TEXTURE28 0x84DC ++#define GL_TEXTURE29 0x84DD ++#define GL_TEXTURE30 0x84DE ++#define GL_TEXTURE31 0x84DF ++#define GL_ACTIVE_TEXTURE 0x84E0 ++#define GL_REPEAT 0x2901 ++#define GL_CLAMP_TO_EDGE 0x812F ++#define GL_MIRRORED_REPEAT 0x8370 ++#define GL_FLOAT_VEC2 0x8B50 ++#define GL_FLOAT_VEC3 0x8B51 ++#define GL_FLOAT_VEC4 0x8B52 ++#define GL_INT_VEC2 0x8B53 ++#define GL_INT_VEC3 0x8B54 ++#define GL_INT_VEC4 0x8B55 ++#define GL_BOOL 0x8B56 ++#define GL_BOOL_VEC2 0x8B57 ++#define GL_BOOL_VEC3 0x8B58 ++#define GL_BOOL_VEC4 0x8B59 ++#define GL_FLOAT_MAT2 0x8B5A ++#define GL_FLOAT_MAT3 0x8B5B ++#define GL_FLOAT_MAT4 0x8B5C ++#define GL_SAMPLER_2D 0x8B5E ++#define GL_SAMPLER_CUBE 0x8B60 ++#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 ++#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 ++#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 ++#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 ++#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A ++#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 ++#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F ++#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A ++#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B ++#define GL_COMPILE_STATUS 0x8B81 ++#define GL_INFO_LOG_LENGTH 0x8B84 ++#define GL_SHADER_SOURCE_LENGTH 0x8B88 ++#define GL_SHADER_COMPILER 0x8DFA ++#define GL_SHADER_BINARY_FORMATS 0x8DF8 ++#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 ++#define GL_LOW_FLOAT 0x8DF0 ++#define GL_MEDIUM_FLOAT 0x8DF1 ++#define GL_HIGH_FLOAT 0x8DF2 ++#define GL_LOW_INT 0x8DF3 ++#define GL_MEDIUM_INT 0x8DF4 ++#define GL_HIGH_INT 0x8DF5 ++#define GL_FRAMEBUFFER 0x8D40 ++#define GL_RENDERBUFFER 0x8D41 ++#define GL_RGBA4 0x8056 ++#define GL_RGB5_A1 0x8057 ++#define GL_RGB565 0x8D62 ++#define GL_DEPTH_COMPONENT16 0x81A5 ++#define GL_STENCIL_INDEX8 0x8D48 ++#define GL_RENDERBUFFER_WIDTH 0x8D42 ++#define GL_RENDERBUFFER_HEIGHT 0x8D43 ++#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 ++#define GL_RENDERBUFFER_RED_SIZE 0x8D50 ++#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 ++#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 ++#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 ++#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 ++#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 ++#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 ++#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 ++#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 ++#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 ++#define GL_COLOR_ATTACHMENT0 0x8CE0 ++#define GL_DEPTH_ATTACHMENT 0x8D00 ++#define GL_STENCIL_ATTACHMENT 0x8D20 ++#define GL_NONE 0 ++#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 ++#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 ++#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 ++#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 ++#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD ++#define GL_FRAMEBUFFER_BINDING 0x8CA6 ++#define GL_RENDERBUFFER_BINDING 0x8CA7 ++#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 ++#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 ++typedef void (GL_APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); ++typedef void (GL_APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); ++typedef void (GL_APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); ++typedef void (GL_APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); ++typedef void (GL_APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); ++typedef void (GL_APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); ++typedef void (GL_APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture); ++typedef void (GL_APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); ++typedef void (GL_APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); ++typedef void (GL_APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); ++typedef void (GL_APIENTRYP PFNGLBLENDFUNCPROC) (GLenum sfactor, GLenum dfactor); ++typedef void (GL_APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); ++typedef void (GL_APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); ++typedef void (GL_APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); ++typedef GLenum (GL_APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); ++typedef void (GL_APIENTRYP PFNGLCLEARPROC) (GLbitfield mask); ++typedef void (GL_APIENTRYP PFNGLCLEARCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); ++typedef void (GL_APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d); ++typedef void (GL_APIENTRYP PFNGLCLEARSTENCILPROC) (GLint s); ++typedef void (GL_APIENTRYP PFNGLCOLORMASKPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); ++typedef void (GL_APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); ++typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); ++typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); ++typedef void (GL_APIENTRYP PFNGLCOPYTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); ++typedef void (GL_APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); ++typedef GLuint (GL_APIENTRYP PFNGLCREATEPROGRAMPROC) (void); ++typedef GLuint (GL_APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); ++typedef void (GL_APIENTRYP PFNGLCULLFACEPROC) (GLenum mode); ++typedef void (GL_APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); ++typedef void (GL_APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); ++typedef void (GL_APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); ++typedef void (GL_APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); ++typedef void (GL_APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); ++typedef void (GL_APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures); ++typedef void (GL_APIENTRYP PFNGLDEPTHFUNCPROC) (GLenum func); ++typedef void (GL_APIENTRYP PFNGLDEPTHMASKPROC) (GLboolean flag); ++typedef void (GL_APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); ++typedef void (GL_APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); ++typedef void (GL_APIENTRYP PFNGLDISABLEPROC) (GLenum cap); ++typedef void (GL_APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); ++typedef void (GL_APIENTRYP PFNGLDRAWARRAYSPROC) (GLenum mode, GLint first, GLsizei count); ++typedef void (GL_APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices); ++typedef void (GL_APIENTRYP PFNGLENABLEPROC) (GLenum cap); ++typedef void (GL_APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); ++typedef void (GL_APIENTRYP PFNGLFINISHPROC) (void); ++typedef void (GL_APIENTRYP PFNGLFLUSHPROC) (void); ++typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); ++typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); ++typedef void (GL_APIENTRYP PFNGLFRONTFACEPROC) (GLenum mode); ++typedef void (GL_APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); ++typedef void (GL_APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); ++typedef void (GL_APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); ++typedef void (GL_APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); ++typedef void (GL_APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures); ++typedef void (GL_APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); ++typedef void (GL_APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); ++typedef void (GL_APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); ++typedef GLint (GL_APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); ++typedef void (GL_APIENTRYP PFNGLGETBOOLEANVPROC) (GLenum pname, GLboolean *data); ++typedef void (GL_APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); ++typedef GLenum (GL_APIENTRYP PFNGLGETERRORPROC) (void); ++typedef void (GL_APIENTRYP PFNGLGETFLOATVPROC) (GLenum pname, GLfloat *data); ++typedef void (GL_APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); ++typedef void (GL_APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *data); ++typedef void (GL_APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); ++typedef void (GL_APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); ++typedef void (GL_APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); ++typedef void (GL_APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); ++typedef void (GL_APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); ++typedef void (GL_APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); ++typedef void (GL_APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); ++typedef const GLubyte *(GL_APIENTRYP PFNGLGETSTRINGPROC) (GLenum name); ++typedef void (GL_APIENTRYP PFNGLGETTEXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); ++typedef void (GL_APIENTRYP PFNGLGETTEXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); ++typedef void (GL_APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); ++typedef void (GL_APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); ++typedef GLint (GL_APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); ++typedef void (GL_APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); ++typedef void (GL_APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); ++typedef void (GL_APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer); ++typedef void (GL_APIENTRYP PFNGLHINTPROC) (GLenum target, GLenum mode); ++typedef GLboolean (GL_APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); ++typedef GLboolean (GL_APIENTRYP PFNGLISENABLEDPROC) (GLenum cap); ++typedef GLboolean (GL_APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer); ++typedef GLboolean (GL_APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); ++typedef GLboolean (GL_APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer); ++typedef GLboolean (GL_APIENTRYP PFNGLISSHADERPROC) (GLuint shader); ++typedef GLboolean (GL_APIENTRYP PFNGLISTEXTUREPROC) (GLuint texture); ++typedef void (GL_APIENTRYP PFNGLLINEWIDTHPROC) (GLfloat width); ++typedef void (GL_APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); ++typedef void (GL_APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param); ++typedef void (GL_APIENTRYP PFNGLPOLYGONOFFSETPROC) (GLfloat factor, GLfloat units); ++typedef void (GL_APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); ++typedef void (GL_APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); ++typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); ++typedef void (GL_APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert); ++typedef void (GL_APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height); ++typedef void (GL_APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); ++typedef void (GL_APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); ++typedef void (GL_APIENTRYP PFNGLSTENCILFUNCPROC) (GLenum func, GLint ref, GLuint mask); ++typedef void (GL_APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask); ++typedef void (GL_APIENTRYP PFNGLSTENCILMASKPROC) (GLuint mask); ++typedef void (GL_APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); ++typedef void (GL_APIENTRYP PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum zpass); ++typedef void (GL_APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); ++typedef void (GL_APIENTRYP PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); ++typedef void (GL_APIENTRYP PFNGLTEXPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat param); ++typedef void (GL_APIENTRYP PFNGLTEXPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); ++typedef void (GL_APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); ++typedef void (GL_APIENTRYP PFNGLTEXPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); ++typedef void (GL_APIENTRYP PFNGLTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); ++typedef void (GL_APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); ++typedef void (GL_APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); ++typedef void (GL_APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); ++typedef void (GL_APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); ++typedef void (GL_APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); ++typedef void (GL_APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); ++typedef void (GL_APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); ++typedef void (GL_APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); ++typedef void (GL_APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); ++typedef void (GL_APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); ++typedef void (GL_APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); ++typedef void (GL_APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); ++typedef void (GL_APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); ++typedef void (GL_APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height); ++#if GL_GLES_PROTOTYPES ++GL_APICALL void GL_APIENTRY glActiveTexture (GLenum texture); ++GL_APICALL void GL_APIENTRY glAttachShader (GLuint program, GLuint shader); ++GL_APICALL void GL_APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); ++GL_APICALL void GL_APIENTRY glBindBuffer (GLenum target, GLuint buffer); ++GL_APICALL void GL_APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); ++GL_APICALL void GL_APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); ++GL_APICALL void GL_APIENTRY glBindTexture (GLenum target, GLuint texture); ++GL_APICALL void GL_APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); ++GL_APICALL void GL_APIENTRY glBlendEquation (GLenum mode); ++GL_APICALL void GL_APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); ++GL_APICALL void GL_APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor); ++GL_APICALL void GL_APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); ++GL_APICALL void GL_APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); ++GL_APICALL void GL_APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); ++GL_APICALL GLenum GL_APIENTRY glCheckFramebufferStatus (GLenum target); ++GL_APICALL void GL_APIENTRY glClear (GLbitfield mask); ++GL_APICALL void GL_APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); ++GL_APICALL void GL_APIENTRY glClearDepthf (GLfloat d); ++GL_APICALL void GL_APIENTRY glClearStencil (GLint s); ++GL_APICALL void GL_APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); ++GL_APICALL void GL_APIENTRY glCompileShader (GLuint shader); ++GL_APICALL void GL_APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); ++GL_APICALL void GL_APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); ++GL_APICALL void GL_APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); ++GL_APICALL void GL_APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); ++GL_APICALL GLuint GL_APIENTRY glCreateProgram (void); ++GL_APICALL GLuint GL_APIENTRY glCreateShader (GLenum type); ++GL_APICALL void GL_APIENTRY glCullFace (GLenum mode); ++GL_APICALL void GL_APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); ++GL_APICALL void GL_APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); ++GL_APICALL void GL_APIENTRY glDeleteProgram (GLuint program); ++GL_APICALL void GL_APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); ++GL_APICALL void GL_APIENTRY glDeleteShader (GLuint shader); ++GL_APICALL void GL_APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures); ++GL_APICALL void GL_APIENTRY glDepthFunc (GLenum func); ++GL_APICALL void GL_APIENTRY glDepthMask (GLboolean flag); ++GL_APICALL void GL_APIENTRY glDepthRangef (GLfloat n, GLfloat f); ++GL_APICALL void GL_APIENTRY glDetachShader (GLuint program, GLuint shader); ++GL_APICALL void GL_APIENTRY glDisable (GLenum cap); ++GL_APICALL void GL_APIENTRY glDisableVertexAttribArray (GLuint index); ++GL_APICALL void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count); ++GL_APICALL void GL_APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices); ++GL_APICALL void GL_APIENTRY glEnable (GLenum cap); ++GL_APICALL void GL_APIENTRY glEnableVertexAttribArray (GLuint index); ++GL_APICALL void GL_APIENTRY glFinish (void); ++GL_APICALL void GL_APIENTRY glFlush (void); ++GL_APICALL void GL_APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); ++GL_APICALL void GL_APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); ++GL_APICALL void GL_APIENTRY glFrontFace (GLenum mode); ++GL_APICALL void GL_APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); ++GL_APICALL void GL_APIENTRY glGenerateMipmap (GLenum target); ++GL_APICALL void GL_APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers); ++GL_APICALL void GL_APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); ++GL_APICALL void GL_APIENTRY glGenTextures (GLsizei n, GLuint *textures); ++GL_APICALL void GL_APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); ++GL_APICALL void GL_APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); ++GL_APICALL void GL_APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); ++GL_APICALL GLint GL_APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); ++GL_APICALL void GL_APIENTRY glGetBooleanv (GLenum pname, GLboolean *data); ++GL_APICALL void GL_APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); ++GL_APICALL GLenum GL_APIENTRY glGetError (void); ++GL_APICALL void GL_APIENTRY glGetFloatv (GLenum pname, GLfloat *data); ++GL_APICALL void GL_APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); ++GL_APICALL void GL_APIENTRY glGetIntegerv (GLenum pname, GLint *data); ++GL_APICALL void GL_APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); ++GL_APICALL void GL_APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); ++GL_APICALL void GL_APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); ++GL_APICALL void GL_APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); ++GL_APICALL void GL_APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); ++GL_APICALL void GL_APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); ++GL_APICALL void GL_APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); ++GL_APICALL const GLubyte *GL_APIENTRY glGetString (GLenum name); ++GL_APICALL void GL_APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat *params); ++GL_APICALL void GL_APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint *params); ++GL_APICALL void GL_APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params); ++GL_APICALL void GL_APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params); ++GL_APICALL GLint GL_APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); ++GL_APICALL void GL_APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); ++GL_APICALL void GL_APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); ++GL_APICALL void GL_APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer); ++GL_APICALL void GL_APIENTRY glHint (GLenum target, GLenum mode); ++GL_APICALL GLboolean GL_APIENTRY glIsBuffer (GLuint buffer); ++GL_APICALL GLboolean GL_APIENTRY glIsEnabled (GLenum cap); ++GL_APICALL GLboolean GL_APIENTRY glIsFramebuffer (GLuint framebuffer); ++GL_APICALL GLboolean GL_APIENTRY glIsProgram (GLuint program); ++GL_APICALL GLboolean GL_APIENTRY glIsRenderbuffer (GLuint renderbuffer); ++GL_APICALL GLboolean GL_APIENTRY glIsShader (GLuint shader); ++GL_APICALL GLboolean GL_APIENTRY glIsTexture (GLuint texture); ++GL_APICALL void GL_APIENTRY glLineWidth (GLfloat width); ++GL_APICALL void GL_APIENTRY glLinkProgram (GLuint program); ++GL_APICALL void GL_APIENTRY glPixelStorei (GLenum pname, GLint param); ++GL_APICALL void GL_APIENTRY glPolygonOffset (GLfloat factor, GLfloat units); ++GL_APICALL void GL_APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); ++GL_APICALL void GL_APIENTRY glReleaseShaderCompiler (void); ++GL_APICALL void GL_APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); ++GL_APICALL void GL_APIENTRY glSampleCoverage (GLfloat value, GLboolean invert); ++GL_APICALL void GL_APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); ++GL_APICALL void GL_APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); ++GL_APICALL void GL_APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); ++GL_APICALL void GL_APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask); ++GL_APICALL void GL_APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); ++GL_APICALL void GL_APIENTRY glStencilMask (GLuint mask); ++GL_APICALL void GL_APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); ++GL_APICALL void GL_APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); ++GL_APICALL void GL_APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); ++GL_APICALL void GL_APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); ++GL_APICALL void GL_APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param); ++GL_APICALL void GL_APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params); ++GL_APICALL void GL_APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param); ++GL_APICALL void GL_APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint *params); ++GL_APICALL void GL_APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); ++GL_APICALL void GL_APIENTRY glUniform1f (GLint location, GLfloat v0); ++GL_APICALL void GL_APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value); ++GL_APICALL void GL_APIENTRY glUniform1i (GLint location, GLint v0); ++GL_APICALL void GL_APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value); ++GL_APICALL void GL_APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); ++GL_APICALL void GL_APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value); ++GL_APICALL void GL_APIENTRY glUniform2i (GLint location, GLint v0, GLint v1); ++GL_APICALL void GL_APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value); ++GL_APICALL void GL_APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); ++GL_APICALL void GL_APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value); ++GL_APICALL void GL_APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); ++GL_APICALL void GL_APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value); ++GL_APICALL void GL_APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); ++GL_APICALL void GL_APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value); ++GL_APICALL void GL_APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); ++GL_APICALL void GL_APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value); ++GL_APICALL void GL_APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); ++GL_APICALL void GL_APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); ++GL_APICALL void GL_APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); ++GL_APICALL void GL_APIENTRY glUseProgram (GLuint program); ++GL_APICALL void GL_APIENTRY glValidateProgram (GLuint program); ++GL_APICALL void GL_APIENTRY glVertexAttrib1f (GLuint index, GLfloat x); ++GL_APICALL void GL_APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v); ++GL_APICALL void GL_APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); ++GL_APICALL void GL_APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v); ++GL_APICALL void GL_APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); ++GL_APICALL void GL_APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v); ++GL_APICALL void GL_APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); ++GL_APICALL void GL_APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v); ++GL_APICALL void GL_APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); ++GL_APICALL void GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); ++#endif ++#endif /* GL_ES_VERSION_2_0 */ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/src/gpu/ganesh/gl/egl/gl2platform.h b/src/gpu/ganesh/gl/egl/gl2platform.h +new file mode 100644 +index 0000000000..eb318dc3a3 +--- /dev/null ++++ b/src/gpu/ganesh/gl/egl/gl2platform.h +@@ -0,0 +1,38 @@ ++#ifndef __gl2platform_h_ ++#define __gl2platform_h_ ++ ++/* ++** Copyright (c) 2017 The Khronos Group Inc. ++** ++** 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. ++*/ ++ ++/* Platform-specific types and definitions for OpenGL ES 2.X gl2.h ++ * ++ * Adopters may modify khrplatform.h and this file to suit their platform. ++ * Please contribute modifications back to Khronos as pull requests on the ++ * public github repository: ++ * https://github.com/KhronosGroup/OpenGL-Registry ++ */ ++ ++#include ++ ++#ifndef GL_APICALL ++#define GL_APICALL KHRONOS_APICALL ++#endif ++ ++#ifndef GL_APIENTRY ++#define GL_APIENTRY KHRONOS_APIENTRY ++#endif ++ ++#endif /* __gl2platform_h_ */ +diff --git a/src/ports/skia_ohos/FontConfig_ohos.cpp b/src/ports/skia_ohos/FontConfig_ohos.cpp +new file mode 100644 +index 0000000000..10c967ee02 +--- /dev/null ++++ b/src/ports/skia_ohos/FontConfig_ohos.cpp +@@ -0,0 +1,1308 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "FontConfig_ohos.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "SkFontStyle.h" ++#include "SkString.h" ++ ++using namespace ErrorCode; ++#if defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_WIN) or defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_MAC) or defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_LINUX) ++static const char* OHOS_DEFAULT_CONFIG = "fontconfig.json"; ++#else ++static const char* OHOS_DEFAULT_CONFIG = "/system/etc/fontconfig.json"; ++#endif ++ ++static const char* PRODUCT_DEFAULT_CONFIG = "/system/etc/productfontconfig.json"; ++/*! Constructor ++ * \param fontScanner the scanner to get the font information from a font file ++ * \param fname the full name of system font configuration document. ++ * \n The default value is '/system/etc/fontconfig.json', if fname is given null ++ */ ++FontConfig_OHOS::FontConfig_OHOS(const SkTypeface_FreeType::Scanner& fontScanner, ++ const char* fname) ++{ ++ int err = checkProductFile(fname); ++ if (err != NO_ERROR) { ++ return; ++ } ++ scanFonts(fontScanner); ++ resetGenericValue(); ++ resetFallbackValue(); ++} ++ ++/*! To get the fallbackForMap ++ * \return The reference of fallbackForMap ++ */ ++const FallbackForMap& FontConfig_OHOS::getFallbackForMap() const ++{ ++ return fallbackForMap; ++} ++ ++/*! To get the fallback set ++ * \return The reference of fallbackSet ++ */ ++const FallbackSet& FontConfig_OHOS::getFallbackSet() const ++{ ++ return fallbackSet; ++} ++ ++/*! To get the count of font style sets supported in the system ++ * \return The count of font style sets in generic family ++ */ ++int FontConfig_OHOS::getFamilyCount() const ++{ ++ return genericFamilySet.size(); ++} ++ ++/*! To get the family name of the default font style set ++ * \param[out] familyName a pointer of SkString object, to which the family value will be set. ++ * \return The count of typeface in this font style set ++ * \n Return -1, if there is no any font style set in the system. ++ */ ++int FontConfig_OHOS::getDefaultFamily(SkString* familyName) const ++{ ++ return getFamilyName(0, familyName); ++} ++ ++/*! To get the family name of a font style set ++ * \param index the index of a font style set in generic family ++ * \param[out] familyName a pointer of SkString object, to which the family value will be set ++ * \return The count of typeface in the font style set ++ * \n Return -1, if the 'index' is out of range ++ */ ++int FontConfig_OHOS::getFamilyName(int index, SkString* familyName) const ++{ ++ if (index < 0 || index >= this->getFamilyCount()) { ++ if (familyName) { ++ familyName->reset(); ++ } ++ return -1; ++ } ++ if (familyName) { ++ *familyName = genericFamilySet[index]->familyName; ++ } ++ return genericFamilySet[index]->typefaceSet->size(); ++} ++ ++/*! To get the count of a font style set ++ * \param styleIndex the index of a font style set ++ * \param isFallback to indicate the font style set is from generic family or fallback family ++ * \n false , the font style set is from generic family list ++ * \n true, the font style set is from fallback family list ++ * \return The count of typeface in the font style set ++ */ ++int FontConfig_OHOS::getTypefaceCount(int styleIndex, bool isFallback) const ++{ ++ if (styleIndex < 0) { ++ return -1; ++ } ++ if (isFallback) { ++ if ((unsigned int)styleIndex < fallbackSet.size()) { ++ return fallbackSet[styleIndex]->typefaceSet->size(); ++ } ++ } else { ++ if ((unsigned int)styleIndex < genericFamilySet.size()) { ++ return genericFamilySet[styleIndex]->typefaceSet->size(); ++ } ++ } ++ return -1; ++} ++ ++/*! To get a typeface ++ * \param styleIndex the index of a font style set ++ * \param index the index of a typeface in its style set ++ * \param isFallback false, the font style set is generic ++ * \n true, the font style set is fallback ++ * \return The pointer of a typeface ++ * \n Return null, if 'styleIndex' or 'index' is out of range ++ */ ++SkTypeface_OHOS* FontConfig_OHOS::getTypeface(int styleIndex, int index, ++ bool isFallback) const ++{ ++ if (styleIndex < 0 || index < 0 || ++ (isFallback && (unsigned int)styleIndex >= fallbackSet.size()) || ++ (!isFallback && (unsigned int)styleIndex >= genericFamilySet.size())) { ++ return nullptr; ++ } ++ if (isFallback) { ++ const TypefaceSet& tpSet = *(fallbackSet[styleIndex]->typefaceSet.get()); ++ if ((unsigned int)index < tpSet.size()) { ++ return tpSet[index].get(); ++ } ++ } else { ++ const TypefaceSet& tpSet = *(genericFamilySet[styleIndex]->typefaceSet.get()); ++ if ((unsigned int)index < tpSet.size()) { ++ return tpSet[index].get(); ++ } ++ } ++ return nullptr; ++} ++ ++/*! To get a typeface ++ * \param styleIndex the index a font style set ++ * \param style the font style to be matching ++ * \param isFallback false, the font style set is generic ++ * \n true, the font style set is fallback ++ * \return An object of typeface whose font style is the closest matching to 'style' ++ * \n Return null, if 'styleIndex' is out of range ++ */ ++SkTypeface_OHOS* FontConfig_OHOS::getTypeface(int styleIndex, const SkFontStyle& style, ++ bool isFallback) const ++{ ++ if (styleIndex < 0 || ++ (isFallback && (unsigned int)styleIndex >= fallbackSet.size()) || ++ (!isFallback && (unsigned int)styleIndex >= genericFamilySet.size())) { ++ return nullptr; ++ } ++ const TypefaceSet* pSet = nullptr; ++ if (isFallback) { ++ pSet = fallbackSet[styleIndex]->typefaceSet.get(); ++ } else { ++ pSet = genericFamilySet[styleIndex]->typefaceSet.get(); ++ } ++ sk_sp tp = matchFontStyle(*pSet, style); ++ return tp.get(); ++} ++ ++/*! To get the index of a font style set ++ * \param familyName the family name of the font style set ++ * \n get the index of default font style set, if 'familyName' is null ++ * \param[out] isFallback to tell if the family is from generic or fallback to the caller. ++ * \n isFallback is false, if the font style is from generic family list ++ * \n isFallback is true, if the font style is from fallback family list ++ * \return The index of the font style set ++ * \n Return -1, if 'familyName' is not found in the system ++ */ ++int FontConfig_OHOS::getStyleIndex(const char* familyName, bool& isFallback) const ++{ ++ if (familyName == nullptr) { ++ isFallback = false; ++ return 0; ++ } ++ SkString fname(familyName); ++ int* p = genericNames.find(fname); ++ if (p) { ++ isFallback = false; ++ return *p; ++ } else { ++ p = fallbackNames.find(fname); ++ if (p) { ++ isFallback = true; ++ return *p; ++ } ++ } ++ return -1; ++} ++ ++/*! Find the closest matching typeface ++ * \param typefaceSet a typeface set belonging to the same font style set ++ * \param pattern the font style to be matching ++ * \return The typeface object which is the closest matching to 'pattern' ++ * \n Return null, if the count of typeface is 0 ++ */ ++sk_sp FontConfig_OHOS::matchFontStyle(const TypefaceSet& typefaceSet, ++ const SkFontStyle& pattern) ++{ ++ int count = typefaceSet.size(); ++ if (count == 1) { ++ return typefaceSet[0]; ++ } ++ sk_sp res = nullptr; ++ uint32_t minDiff = 0xFFFFFFFF; ++ for (int i = 0; i < count; i++) { ++ const SkFontStyle& fontStyle = typefaceSet[i]->fontStyle(); ++ uint32_t diff = getFontStyleDifference(pattern, fontStyle); ++ if (diff < minDiff) { ++ minDiff = diff; ++ res = typefaceSet[i]; ++ } ++ } ++ return res; ++} ++ ++/*! To get the difference between a font style and the matching font style ++ * \param dstStyle the style to be matching ++ * \param srcStyle a font style ++ * \return The difference value of a specified style with the matching style ++ */ ++uint32_t FontConfig_OHOS::getFontStyleDifference(const SkFontStyle& dstStyle, ++ const SkFontStyle& srcStyle) ++{ ++ int normalWidth = SkFontStyle::kNormal_Width; ++ int dstWidth = dstStyle.width(); ++ int srcWidth = srcStyle.width(); ++ ++ uint32_t widthDiff = 0; ++ // The maximum font width is kUltraExpanded_Width i.e. '9'. ++ // If dstWidth <= kNormal_Width (5), first check narrower values, then wider values. ++ // If dstWidth > kNormal_Width, first check wider values, then narrower values. ++ // When dstWidth and srcWidth are at different side of kNormal_Width, ++ // the width difference between them should be more than 5 (9/2+1) ++ if (dstWidth <= normalWidth) { ++ if (srcWidth <= dstWidth) { ++ widthDiff = dstWidth - srcWidth; ++ } else { ++ widthDiff = srcWidth - dstWidth + 5; ++ } ++ } else { ++ if (srcWidth >= dstWidth) { ++ widthDiff = srcWidth - dstWidth; ++ } else { ++ widthDiff = dstWidth - srcWidth + 5; ++ } ++ } ++ ++ int diffSlantValue[3][3] = { ++ {0, 2, 1}, ++ {2, 0, 1}, ++ {2, 1, 0} ++ }; ++ uint32_t slantDiff = diffSlantValue[dstStyle.slant()][srcStyle.slant()]; ++ ++ int dstWeight = dstStyle.weight(); ++ int srcWeight = srcStyle.weight(); ++ uint32_t weightDiff = 0; ++ ++ // The maximum weight is kExtraBlack_Weight (1000), when dstWeight and srcWeight are at the different ++ // side of kNormal_Weight, the weight difference between them should be more than 500 (1000/2) ++ if ((dstWeight == SkFontStyle::kNormal_Weight && srcWeight == SkFontStyle::kMedium_Weight) || ++ (dstWeight == SkFontStyle::kMedium_Weight && srcWeight == SkFontStyle::kNormal_Weight)) { ++ weightDiff = 50; ++ } else if (dstWeight <= SkFontStyle::kNormal_Weight) { ++ if (srcWeight <= dstWeight) { ++ weightDiff = dstWeight - srcWeight; ++ } else { ++ weightDiff = srcWeight - dstWeight + 500; ++ } ++ } else if (dstWeight > SkFontStyle::kNormal_Weight) { ++ if (srcWeight >= dstWeight) { ++ weightDiff = srcWeight - dstWeight; ++ } else { ++ weightDiff = dstWeight - srcWeight + 500; ++ } ++ } ++ // The first 2 bytes to save weight difference, the third byte to save slant difference, ++ // and the fourth byte to save width difference ++ uint32_t diff = (widthDiff << 24) + (slantDiff << 16) + weightDiff; ++ return diff; ++} ++ ++/*! To get the data of font configuration file ++ * \param fname the full name of the font configuration file ++ * \param[out] size the size of data returned to the caller ++ * \return The pointer of content of the file ++ * \note The returned pointer should be freed by the caller ++ */ ++char* FontConfig_OHOS::getFileData(const char* fname, int& size) ++{ ++ FILE* fp = fopen(fname, "r"); ++ if (fp == nullptr) { ++ return nullptr; ++ } ++ fseek(fp, 0L, SEEK_END); ++ size = ftell(fp) + 1; ++ rewind(fp); ++ void* data = malloc(size); ++ if (data == nullptr) { ++ fclose(fp); ++ return nullptr; ++ } ++ memset(data, 0, size); ++ (void) fread(data, size, 1, fp); ++ fclose(fp); ++ return (char*)data; ++} ++ ++/*! parse the system font configuration document ++ * \param fname the full name of the font configuration document ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_NOT_FOUND config document is not found ++ * \return ERROR_CONFIG_FORMAT_NOT_SUPPORTED config document format is not supported ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE wrong type of value in the configuration ++ */ ++int FontConfig_OHOS::parseConfig(const char* fname) ++{ ++ if (fname == nullptr) { ++ fname = OHOS_DEFAULT_CONFIG; ++ } ++ Json::Value root; ++ int err = checkConfigFile(fname, root); ++ if (err != NO_ERROR) { ++ return err; ++ } ++ // "fontdir" - optional, the data type should be string ++ const char* key = "fontdir"; ++ if (root.isMember(key)) { ++ if (root[key].isArray()) { ++ parseFontDir(root[key]); ++ } else { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key); ++ } ++ } ++ // "generic", "fallback" - necessary, the data type should be array ++ const char* keys[] = {"generic", "fallback", nullptr}; ++ int index = 0; ++ while (true) { ++ if (keys[index] == nullptr) { ++ break; ++ } ++ key = keys[index++]; ++ if (!root.isMember(key)) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, key); ++ } else if (!root[key].isArray()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::arrayValue, root[key].type()); ++ } ++ const Json::Value& arr = root[key]; ++ for (unsigned int i = 0; i < arr.size(); i++) { ++ if (arr[i].isObject()) { ++ if (!strcmp(key, "generic")) { ++ parseGeneric(arr[i]); ++ } else if (!strcmp(key, "fallback")) { ++ parseFallback(arr[i]); ++ } ++ } else { ++ SkString errKey; ++ errKey.appendf("%s#%d", key, i + 1); ++ (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, errKey.c_str(), ++ Json::objectValue, arr[i].type()); ++ } ++ } ++ } ++ root.clear(); ++ return NO_ERROR; ++} ++ ++/*! check the system font configuration document ++ * \param fname the full name of the font configuration document ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_NOT_FOUND config document is not found ++ * \return ERROR_CONFIG_FORMAT_NOT_SUPPORTED config document format is not supported ++ */ ++int FontConfig_OHOS::checkConfigFile(const char* fname, Json::Value& root) ++{ ++ int size = 0; ++ char* data = getFileData(fname, size); ++ if (data == nullptr) { ++ return logErrInfo(ERROR_CONFIG_NOT_FOUND, fname); ++ } ++ std::string errs; ++ Json::CharReaderBuilder charReaderBuilder; ++ std::unique_ptr jsonReader(charReaderBuilder.newCharReader()); ++ bool isJson = jsonReader->parse(data, data + size, &root, &errs); ++ free((void*)data); ++ data = nullptr; ++ ++ if (!isJson || !errs.empty()) { ++ return logErrInfo(ERROR_CONFIG_FORMAT_NOT_SUPPORTED, fname); ++ } ++ return NO_ERROR; ++} ++#if ENABLE_DEBUG ++/*! To print out the font information ++ * \param font the font object to be printed ++ */ ++void FontConfig_OHOS::dumpFont(const FontInfo& font) const ++{ ++ LOGI("name=%s, family=%s, weight=%d, width=%d, slant=%d, index=%d", ++ font.fname.c_str(), font.familyName.c_str(), font.style.weight(), font.style.width(), font.style.slant(), ++ font.index); ++ int count = font.axisSet.axis.size(); ++ if (count > 0) { ++ SkString str; ++ for (unsigned int i = 0; i < count; i++) { ++ str.appendU32(SkFixedFloorToInt(font.axisSet.axis[i])); ++ if (i < count - 1) { ++ str.append(","); ++ } ++ } ++ LOGI("axis={%s}\n", str.c_str()); ++ } ++} ++ ++/*! To print out the information of generic font style set ++ */ ++void FontConfig_OHOS::dumpGeneric() const ++{ ++ LOGI("\n"); ++ for (unsigned int i = 0; i < genericFamilySet.size(); i++) { ++ LOGI("[%d] familyName : %s - %d\n", i, genericFamilySet[i]->familyName.c_str(), ++ static_cast(genericFamilySet[i]->typefaceSet->size())); ++ for (int j = 0; j < genericFamilySet[i]->typefaceSet->size(); j++) { ++ if ((*(genericFamilySet[i]->typefaceSet))[j].get()) { ++ const FontInfo* font = (*(genericFamilySet[i]->typefaceSet))[j]->getFontInfo(); ++ if (font) { ++ dumpFont(*font); ++ } else { ++ LOGE("font [%d] is null\n", j); ++ } ++ } else { ++ LOGE("typefeace [%d] is null\n", j); ++ } ++ } ++ } ++} ++ ++/*! To print out the information of fallback font style set ++ */ ++void FontConfig_OHOS::dumpFallback() const ++{ ++ LOGI("\n"); ++ int count = 0; ++ fallbackForMap.foreach([this, &count](const SkString& key, ++ const FallbackSetPos& setIndex) { ++ LOGI("[%d] family : %s - %d\n", count++, key.c_str(), setIndex.count); ++ for (unsigned int i = setIndex.index; i < setIndex.index + setIndex.count; i++) { ++ const TypefaceSet& tpSet = *(fallbackSet[i]->typefaceSet.get()); ++ LOGI("[%s] - %d\n", fallbackSet[i]->familyName.c_str(), static_cast(tpSet.size())); ++ ++ for (unsigned int j = 0; j < tpSet.size(); j++) { ++ const FontInfo* font = tpSet[j]->getFontInfo(); ++ if (font) { ++ this->dumpFont(*font); ++ } else { ++ LOGE("font [%d] is null\n", j); ++ } ++ } ++ } ++ }); ++} ++#endif ++ ++/*! To parse 'fontdir' attribute ++ * \param root the root node of 'fontdir' ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type ++ */ ++int FontConfig_OHOS::parseFontDir(const Json::Value& root) ++{ ++ for (unsigned int i = 0; i < root.size(); i++) { ++ if (root[i].isString()) { ++ const char* dir = root[i].asCString(); ++ fontDirSet.emplace_back(SkString(dir)); ++ } else { ++ SkString text; ++ text.appendf("fontdir#%d", i + 1); ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, text.c_str(), Json::stringValue, root[i].type()); ++ } ++ } ++ return NO_ERROR; ++} ++ ++/*! To parse an item of 'generic' family ++ * \param root the root node of an item in 'generic' list ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute ++ * \return ERROR_CONFIG_MISSING_TAG missing tag of 'family' or 'alias' ++ */ ++int FontConfig_OHOS::parseGeneric(const Json::Value& root) ++{ ++ // "family" - necessary, the data type should be String ++ const char* key = "family"; ++ if (!root.isMember(key)) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, key); ++ } else if (!root[key].isString()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::stringValue, root[key].type()); ++ } ++ SkString familyName = SkString(root[key].asCString()); ++ // "alias" - necessary, the data type should be Array ++ if (!root.isMember("alias")) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, "alias"); ++ } ++ // "adjust", "variation" - optional ++ const char* tags[] = {"alias", "adjust", "variations", "index"}; ++ std::vector aliasSet; ++ std::vector adjustSet; ++ std::vector variationSet; ++ for (unsigned int i = 0; i < sizeof(tags) / sizeof(char*); i++) { ++ key = tags[i]; ++ if (!root.isMember(key)) { ++ continue; ++ } ++ if (root[key].isArray()) { ++ if (!strcmp(key, "index")) { ++ parseTtcIndex(root[key], familyName); ++ continue; ++ } ++ const Json::Value& arr = root[key]; ++ for (unsigned int j = 0; j < arr.size(); j++) { ++ if (arr[j].isObject()) { ++ if (!strcmp(key, "alias")) { ++ parseAlias(arr[j], aliasSet); ++ } else if (!strcmp(key, "adjust")) { ++ parseAdjust(arr[j], adjustSet); ++ } else { ++ parseVariation(arr[j], variationSet); ++ } ++ } else { ++ SkString text; ++ text.appendf("%s#%d", key, j + 1); ++ (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, text.c_str(), Json::objectValue, ++ arr[j].type()); ++ } ++ } ++ } else { ++ (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::arrayValue, root[key].type()); ++ } ++ if (root.size() == 2) { ++ break; ++ } ++ } ++ if (aliasSet.size()) { ++ aliasMap.set(SkString(familyName), aliasSet); ++ } ++ if (adjustSet.size()) { ++ adjustMap.set(SkString(familyName), adjustSet); ++ } ++ if (variationSet.size()) { ++ variationMap.set(SkString(familyName), variationSet); ++ } ++ return NO_ERROR; ++} ++ ++/*! To parse an item of 'alias' attribute ++ * \param root the root node of an item in an 'alias' list ++ * \param[out] aliasSet the value of AliasInfo will be written to and returned to the caller ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute ++ * \return ERROR_CONFIG_MISSING_TAG missing tag of alias name ++ */ ++int FontConfig_OHOS::parseAlias(const Json::Value& root, std::vector& aliasSet) ++{ ++ if (root.empty()) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, "generic-alias-name"); ++ } ++ Json::Value::Members members = root.getMemberNames(); ++ const char* key = members[0].c_str(); ++ if (!root[key].isInt()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, "generic-alias-weight", ++ Json::intValue, root[key].type()); ++ } ++ ++ SkString aliasName = SkString(key); ++ int weight = root[key].asInt(); ++ std::unique_ptr genericFamily = std::make_unique(); ++ genericFamily->familyName = SkString(key); ++ if (aliasSet.size() == 0 || weight > 0) { ++ genericFamily->typefaceSet = std::make_shared(); ++ } else { ++ int index = aliasSet[0].pos; ++ genericFamily->typefaceSet = genericFamilySet[index]->typefaceSet; ++ } ++ genericNames.set(SkString(genericFamily->familyName), genericFamilySet.size()); ++ ++ AliasInfo info = {static_cast(genericFamilySet.size()), weight}; ++ aliasSet.emplace_back(std::move(info)); ++ genericFamilySet.emplace_back(std::move(genericFamily)); ++ return NO_ERROR; ++} ++ ++/*! To parse an item of 'adjust' attribute ++ * \param root the root node of an item in an 'adjust' list ++ * \param[out] adjustSet the value of AdjustInfo will be written to and returned to the caller ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute ++ * \return ERROR_CONFIG_MISSING_TAG missing tag of 'weight' or 'to' ++ */ ++int FontConfig_OHOS::parseAdjust(const Json::Value& root, std::vector& adjustSet) ++{ ++ const char* tags[] = {"weight", "to"}; ++ int values[2]; // value[0] - to save 'weight', value[1] - to save 'to' ++ for (unsigned int i = 0; i < sizeof(tags) / sizeof(char*); i++) { ++ const char* key = tags[i]; ++ if (!root.isMember(key)) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, key); ++ } else if (!root[key].isInt()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, ++ Json::intValue, root[key].type()); ++ } else { ++ values[i] = root[key].asInt(); ++ } ++ } ++ AdjustInfo info = {values[0], values[1]}; ++ adjustSet.push_back(info); ++ return NO_ERROR; ++} ++ ++/*! To parse an item of 'fallback' attribute ++ * \param root the root node of an item in 'fallback' list ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute ++ * \return ERROR_CONFIG_MISSING_TAG missing tag of fallbackFor ++ */ ++int FontConfig_OHOS::parseFallback(const Json::Value& root) ++{ ++ if (root.empty()) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, "fallback-fallbackFor"); ++ } ++ Json::Value::Members members = root.getMemberNames(); ++ const char* key = members[0].c_str(); ++ if (!root[key].isArray()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, "fallback-items", ++ Json::arrayValue, root[key].type()); ++ } ++ unsigned int startPos = fallbackSet.size(); ++ SkString fallbackFor = SkString(key); ++ const Json::Value& fallbackArr = root[key]; ++ for (unsigned int i = 0; i < fallbackArr.size(); i++) { ++ if (!fallbackArr[i].isObject()) { ++ SkString text; ++ text.appendf("fallback-%s#%d", key, i + 1); ++ (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, text.c_str(), Json::objectValue, ++ fallbackArr[i].type()); ++ continue; ++ } ++ parseFallbackItem(fallbackArr[i]); ++ } ++ FallbackSetPos setPos = {startPos, (unsigned int)(fallbackSet.size() - startPos)}; ++ fallbackForMap.set(fallbackFor, setPos); ++ return NO_ERROR; ++} ++ ++/*! To parse an item of fallback family ++ * \param root the root node of a fallback item ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute ++ * \return ERROR_CONFIG_MISSING_TAG missing tag of language ++ */ ++int FontConfig_OHOS::parseFallbackItem(const Json::Value& root) ++{ ++ if (root.empty()) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, "fallback-item-lang"); ++ } ++ Json::Value::Members members = root.getMemberNames(); ++ const char* key = nullptr; ++ bool hasIndex = false; ++ bool hasVariations = false; ++ for (unsigned int i = 0; i < members.size(); i++) { ++ if (members[i] == "variations") { ++ hasVariations = true; ++ } else if (members[i] == "index") { ++ hasIndex = true; ++ } else { ++ key = members[i].c_str(); ++ } ++ } ++ if (key == nullptr) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, "fallback-item-lang"); ++ } ++ if (!root[key].isString()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, "fallback-item-family", ++ Json::stringValue, root[key].type()); ++ } ++ SkString lang = SkString(key); ++ SkString familyName = SkString(root[key].asCString()); ++ if (hasVariations) { ++ key = "variations"; ++ if (root[key].isArray()) { ++ const Json::Value& varArr = root[key]; ++ std::vector variationSet; ++ for (unsigned int i = 0; i < varArr.size(); i++) { ++ if (varArr[i].isObject()) { ++ parseVariation(varArr[i], variationSet); ++ } else { ++ SkString text = SkString("variations#"); ++ text.appendU32(i + 1); ++ (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, text.c_str(), ++ Json::objectValue, varArr[i].type()); ++ } ++ } ++ if (variationSet.size()) { ++ variationMap.set(SkString(familyName), variationSet); ++ } ++ } else { ++ (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::arrayValue, ++ root[key].type()); ++ } ++ } ++ if (hasIndex) { ++ key = "index"; ++ if (root[key].isArray()) { ++ parseTtcIndex(root[key], familyName); ++ } else { ++ (void) logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::arrayValue, root[key].type()); ++ } ++ } ++ std::unique_ptr fallback = std::make_unique(); ++ fallback->familyName = familyName; ++ fallback->langs = lang; ++ fallback->typefaceSet = std::make_shared(); ++ fallbackNames.set(SkString(familyName), fallbackSet.size()); ++ fallbackSet.emplace_back(std::move(fallback)); ++ return NO_ERROR; ++} ++ ++/*! To parse an item of 'variations' attribute ++ * \param root the root node of an item in 'variations' list ++ * \param[out] variationSet the value of VariationInfo is written to and returned to the caller ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute ++ * \return ERROR_CONFIG_MISSING_TAG missing tag of 'weight' or 'wght' ++ */ ++int FontConfig_OHOS::parseVariation(const Json::Value& root, std::vector& variationSet) ++{ ++ const char* key = nullptr; ++ const char* tags[] = {"wght", "wdth", "slnt", "weight", "width", "slant"}; ++ VariationInfo info; ++ for (unsigned int i = 0; i < sizeof(tags) / sizeof(char*); i++) { ++ key = tags[i]; ++ if ((!strcmp(key, "wght") || !strcmp(key, "weight")) && ++ !root.isMember(key)) { ++ return logErrInfo(ERROR_CONFIG_MISSING_TAG, key); ++ } ++ if (!root.isMember(key)) { ++ continue; ++ } ++ if (!strcmp(key, "weight")) { ++ if (!root[key].isInt()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::intValue, root[key].type()); ++ } ++ info.weight = root[key].asInt(); ++ } else if (!strcmp(key, "width")) { ++ if (!root[key].isInt()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::intValue, root[key].type()); ++ } ++ info.width = root[key].asInt(); ++ } else if (!strcmp(key, "slant")) { ++ if (!root[key].isString()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::stringValue, root[key].type()); ++ } ++ const char* str = root[key].asCString(); ++ if (!strcmp(str, "normal")) { ++ info.slant = static_cast(SkFontStyle::kUpright_Slant); ++ } else if (!strcmp(str, "italic")) { ++ info.slant = static_cast(SkFontStyle::kItalic_Slant); ++ } else if (!strcmp(str, "oblique")) { ++ info.slant = static_cast(SkFontStyle::kOblique_Slant); ++ } ++ } else { ++ if (!root[key].isNumeric()) { ++ return logErrInfo(ERROR_CONFIG_INVALID_VALUE_TYPE, key, Json::realValue, root[key].type()); ++ } ++ Coordinate axis; ++ axis.axis = SkSetFourByteTag(key[0], key[1], key[2], key[3]); ++ axis.value = root[key].asFloat(); ++ info.axis.emplace_back(axis); ++ } ++ } ++ variationSet.emplace_back(info); ++ return NO_ERROR; ++} ++ ++/*! To parse 'index' attribute ++ * \param root the root node of 'index' attribute ++ * \param familyName the name of the family which the root node belongs to ++ * \return NO_ERROR successful ++ * \return ERROR_CONFIG_INVALID_VALUE_TYPE invalid value type for an attribute ++ */ ++int FontConfig_OHOS::parseTtcIndex(const Json::Value& root, const SkString& familyName) ++{ ++ unsigned int keyCount = 2; // the value of 'index' is an array with 2 items. ++ if (root.size() == keyCount && root[0].isString() && root[1].isNumeric()) { ++ TtcIndexInfo item = { SkString(root[0].asCString()), root[1].asInt() }; ++ if (item.ttcIndex != 0 && ttcIndexMap.find(item.familyName) == nullptr) { ++ ttcIndexMap.set(SkString(item.familyName), {SkString(item.familyName), 0}); ++ } ++ ttcIndexMap.set(SkString(familyName), item); ++ } else { ++ int ret = ERROR_CONFIG_INVALID_VALUE_TYPE; ++ SkString text; ++ const char* key = "index"; ++ if (root.size() != keyCount) { ++ text.appendf("%s#0", key); ++ errSet.emplace_back(ret, text.c_str()); ++ LOGE("%s : '%s' size should be 2, but here it's %d\n", errToString(ret), key, root.size()); ++ return ret; ++ } else if (!root[0].isString()) { ++ text.appendf("%s#1", key); ++ return logErrInfo(ret, text.c_str(), Json::stringValue, root[0].type()); ++ } else { ++ text.appendf("%s#2", key); ++ return logErrInfo(ret, text.c_str(), Json::intValue, root[1].type()); ++ } ++ } ++ return NO_ERROR; ++} ++ ++/*! To get the axis value and set to 'font' ++ * \param axisDefs the axis ranges of a font ++ * \param variation the variation data from which axis values are generated ++ * \param[out] font the axis values will be written to and returned to the caller ++ */ ++void FontConfig_OHOS::getAxisValues(const AxisDefinitions& axisDefs, ++ const VariationInfo& variation, FontInfo& font) const ++{ ++ SkFontArguments::VariationPosition position; ++ position.coordinateCount = variation.axis.size(); ++ position.coordinates = variation.axis.data(); ++ ++ int count = axisDefs.size(); ++ SkFixed axisValues[count]; ++ SkTypeface_FreeType::Scanner::computeAxisValues(axisDefs, position, ++ axisValues, font.familyName); ++ font.axisSet.axis.clear(); ++ font.axisSet.range.clear(); ++ for (int i = 0; i < count; i++) { ++ font.axisSet.axis.emplace_back(axisValues[i]); ++ font.axisSet.range.emplace_back(axisDefs[i]); ++ } ++} ++ ++/*! To insert a ttc font into a font style set ++ * \param count the count of typeface in a ttc font ++ * \param font an object of the FontInfo with font information ++ * \return true, if the font is a ttc font and added to corresponding font style set ++ * \return false, if the font is not a ttc font ++ */ ++bool FontConfig_OHOS::insertTtcFont(int count, FontInfo& font) ++{ ++ bool ret = false; ++ ttcIndexMap.foreach([this, count, &font, &ret] ++ (const SkString& familyName, TtcIndexInfo* info) { ++ if (info->familyName == font.familyName && info->ttcIndex < count) { ++ SkString specifiedName; ++ TypefaceSet* tpSet = this->getTypefaceSet(familyName, specifiedName); ++ if (tpSet) { ++ FontInfo newFont(font); ++ newFont.familyName = familyName; ++ newFont.index = info->ttcIndex; ++ sk_sp typeface = sk_make_sp(specifiedName, newFont); ++ tpSet->push_back(std::move(typeface)); ++ ret = true; ++ } ++ } ++ }); ++ return ret; ++} ++ ++/*! To insert a variable font into a font style set ++ * \param axisDefs the axis ranges of a variable font ++ * \param font an object of the FontInfo with font information ++ * \return true, if the font is a variable and some typefaces are added to the corresponding font style set ++ * \return false, if the font is not variable ++ */ ++bool FontConfig_OHOS::insertVariableFont(const AxisDefinitions& axisDefs, FontInfo& font) ++{ ++ const SkString& key = font.familyName; ++ if (variationMap.find(key) == nullptr || axisDefs.size() == 0) { ++ return false; ++ } ++ SkString specifiedName; ++ TypefaceSet* tpSet = getTypefaceSet(key, specifiedName); ++ if (tpSet == nullptr) { ++ return false; ++ } ++ const std::vector& variationSet = *(variationMap.find(key)); ++ for (unsigned int i = 0; i < variationSet.size(); i++) { ++ FontInfo newFont(font); ++ getAxisValues(axisDefs, variationSet[i], newFont); ++ int width = font.style.width(); ++ SkFontStyle::Slant slant = font.style.slant(); ++ if (variationSet[i].width != -1) { ++ width = variationSet[i].width; ++ } ++ if (variationSet[i].slant != -1) { ++ slant = (SkFontStyle::Slant) variationSet[i].slant; ++ } ++ newFont.style = SkFontStyle(variationSet[i].weight, width, slant); ++ sk_sp typeface = sk_make_sp(specifiedName, newFont); ++ tpSet->push_back(std::move(typeface)); ++ } ++ return true; ++} ++ ++/*! To get the typeface set of a font style set ++ * \param familyName the family name of a font style set ++ * \param[out] specifiedName the specified family name of a font style set returned to the caller ++ * \return The object of typeface set ++ * \n Return null, if the family name is not found in the system ++ */ ++TypefaceSet* FontConfig_OHOS::getTypefaceSet(const SkString& familyName, ++ SkString& specifiedName) const ++{ ++ if (aliasMap.find(familyName) != nullptr) { ++ const std::vector& aliasSet = *(aliasMap.find(familyName)); ++ if (aliasSet.size()) { ++ int index = aliasSet[0].pos; ++ specifiedName = genericFamilySet[index]->familyName; ++ return genericFamilySet[index]->typefaceSet.get(); ++ } ++ } else if (fallbackNames.find(familyName) != nullptr) { ++ int index = *(fallbackNames.find(familyName)); ++ return fallbackSet[index]->typefaceSet.get(); ++ } ++ return nullptr; ++} ++ ++/*! To load font information from a font file ++ * \param scanner a scanner used to parse the font file ++ * \param fname the full name of a font file ++ * \return NO_ERROR successful ++ * \return ERROR_FONT_NOT_EXIST font file is not exist ++ * \return ERROR_FONT_INVALID_STREAM the stream is not recognized ++ */ ++int FontConfig_OHOS::loadFont(const SkTypeface_FreeType::Scanner& scanner, const char* fname) ++{ ++ std::unique_ptr stream = SkStream::MakeFromFile(fname); ++ int count = 1; ++ SkTypeface_FreeType::Scanner::AxisDefinitions axisDefs; ++ FontInfo font(fname, 0); ++ if (stream == nullptr || ++ scanner.recognizedFont(stream.get(), &count) == false || ++ scanner.scanFont(stream.get(), 0, &font.familyName, &font.style, ++ &font.isFixedWidth, &axisDefs) == false) { ++ int err = NO_ERROR; ++ if (stream == nullptr) { ++ err = ERROR_FONT_NOT_EXIST; ++ } else { ++ err = ERROR_FONT_INVALID_STREAM; ++ } ++ LOGE("%s : %s\n", errToString(err), fname); ++ char* fnameCopy = strdup(fname); ++ errSet.emplace_back(err, basename(fnameCopy)); ++ free(fnameCopy); ++ return err; ++ } ++ // for adjustMap - update weight ++ if (adjustMap.find(font.familyName) != nullptr) { ++ const std::vector adjustSet = *(adjustMap.find(font.familyName)); ++ for (unsigned int i = 0; i < adjustSet.size(); i++) { ++ if (font.style.weight() == adjustSet[i].origValue) { ++ font.style = SkFontStyle(adjustSet[i].newValue, font.style.width(), font.style.slant()); ++ break; ++ } ++ } ++ } ++ bool ret = false; ++ if (count > 1) { ++ ret = insertTtcFont(count, font); ++ } else if (axisDefs.size() > 0) { ++ ret = insertVariableFont(axisDefs, font); ++ } ++ if (!ret) { ++ SkString specifiedName; ++ TypefaceSet* tpSet = getTypefaceSet(font.familyName, specifiedName); ++ if (tpSet) { ++ sk_sp typeface = sk_make_sp(specifiedName, font); ++ tpSet->push_back(std::move(typeface)); ++ } ++ } ++ return NO_ERROR; ++} ++ ++/*! To scan the system font directories ++ * \param fontScanner the scanner used to parse a font file ++ * \return NO_ERROR success ++ * \return ERROR_DIR_NOT_FOUND a font directory is not exist ++ */ ++int FontConfig_OHOS::scanFonts(const SkTypeface_FreeType::Scanner& fontScanner) ++{ ++ int err = NO_ERROR; ++ if (fontDirSet.size() == 0) { ++ fontDirSet.emplace_back(SkString("/system/fonts/")); ++ } ++ for (unsigned int i = 0; i < fontDirSet.size(); i++) { ++ DIR* dir = opendir(fontDirSet[i].c_str()); ++ if (dir == nullptr) { ++ err = logErrInfo(ERROR_DIR_NOT_FOUND, fontDirSet[i].c_str()); ++ continue; ++ } ++ struct dirent* node = nullptr; ++#if defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_WIN) ++ struct stat filestat; ++#endif ++ while ((node = readdir(dir))) { ++#if defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_WIN) ++ stat(node->d_name, &filestat); ++ if(S_ISDIR(filestat.st_mode)) { ++ continue; ++ } ++#else ++ if (node->d_type != DT_REG) { ++ continue; ++ } ++#endif ++ const char* fname = node->d_name; ++ int len = strlen(fname); ++ int suffixLen = strlen(".ttf"); ++ if (len < suffixLen || (strncmp(fname + len - suffixLen, ".ttf", suffixLen) && ++ strncmp(fname + len - suffixLen, ".otf", suffixLen) && ++ strncmp(fname + len - suffixLen, ".ttc", suffixLen) && ++ strncmp(fname + len - suffixLen, ".otc", suffixLen))) { ++ continue; ++ } ++ len += (fontDirSet[i].size() + 2); // 2 more characters for '/' and '\0' ++ char fullname[len]; ++ memset(fullname, 0, len); ++ strncpy(fullname, fontDirSet[i].c_str(), len); ++#if defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_WIN) ++ if (fontDirSet[i][fontDirSet[i].size() - 1] != '\\') { ++ strcat(fullname, "\\"); ++ } ++#else ++ if (fontDirSet[i][fontDirSet[i].size() - 1] != '/') { ++ strcat(fullname, "/"); ++ } ++#endif ++ strcat(fullname, fname); ++ loadFont(fontScanner, fullname); ++ } ++ closedir(dir); ++ } ++ fontDirSet.clear(); ++ return err; ++} ++ ++/*! To reset the generic family ++ * \n 1. To sort the typefaces for each font style set in generic list ++ * \n 2. To build typeface set for those font style sets which have single weight value ++ */ ++void FontConfig_OHOS::resetGenericValue() ++{ ++ aliasMap.foreach([this](SkString& key, std::vector* pAliasSet) { ++ std::vector& aliasSet = *pAliasSet; ++ int index = aliasSet[0].pos; ++ if (genericFamilySet[index]->typefaceSet->size() == 0) { ++ this->logErrInfo(ERROR_FAMILY_NOT_FOUND, key.c_str()); ++ } else { ++ sortTypefaceSet(genericFamilySet[index]->typefaceSet); ++ for (unsigned int i = 1; i < aliasSet.size(); i++) { ++ if (aliasSet[i].weight == 0) { ++ continue; ++ } ++ buildSubTypefaceSet(genericFamilySet[index]->typefaceSet, ++ genericFamilySet[index + i]->typefaceSet, ++ genericFamilySet[index + i]->familyName, ++ aliasSet[i].weight); ++ if (genericFamilySet[index + i]->typefaceSet->size() == 0) { ++ this->logErrInfo(ERROR_FAMILY_NOT_FOUND, ++ genericFamilySet[index + i]->familyName.c_str()); ++ } ++ } ++ } ++ }); ++ ++ aliasMap.reset(); ++ adjustMap.reset(); ++ variationMap.reset(); ++ ttcIndexMap.reset(); ++} ++ ++/*! To build a sub typeface set according to weight from a typeface set ++ * \param typefaceSet the parent typeface set ++ * \param[out] subSet the sub typeface set returned to the caller ++ * \param familyName the family name of the sub typeface set ++ * \param weight the weight of the sub typeface set ++ */ ++void FontConfig_OHOS::buildSubTypefaceSet(const std::shared_ptr& typefaceSet, ++ std::shared_ptr& subSet, const SkString& familyName, int weight) ++{ ++ if (typefaceSet->size() == 0) { ++ return; ++ } ++ for (unsigned int i = 0; i < typefaceSet->size(); i++) { ++ const SkTypeface_OHOS* typeface = (*typefaceSet)[i].get(); ++ if (typeface && typeface->fontStyle().weight() == weight) { ++ const FontInfo* pFont = typeface->getFontInfo(); ++ if (pFont == nullptr) { ++ continue; ++ } ++ FontInfo font(*pFont); ++ sk_sp newTypeface = sk_make_sp(familyName, font); ++ subSet->push_back(std::move(newTypeface)); ++ } ++ } ++} ++ ++/*! To reset the fallback value ++ * \n To sort the typefaces for each font style set in fallback list. ++ */ ++void FontConfig_OHOS::resetFallbackValue() ++{ ++ for (unsigned int i = 0; i < fallbackSet.size(); i++) { ++ if (fallbackSet[i]->typefaceSet->size() == 0) { ++ logErrInfo(ERROR_FAMILY_NOT_FOUND, fallbackSet[i]->familyName.c_str()); ++ } ++ sortTypefaceSet(fallbackSet[i]->typefaceSet); ++ } ++} ++ ++/*! To check if an error happened ++ * \param err the id of an error ++ * \param text the key to indicate the part with the error happened ++ * \return false, this kind of error did not happen ++ * \return true, the error happened ++ */ ++bool FontConfig_OHOS::hasError(int err, const SkString& text) const ++{ ++ for (unsigned int i = 0; i < errSet.size(); i++) { ++ if (errSet[i].err == err && errSet[i].text == text) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++/*! To get the total count of errors happened ++ * \return The count of errors ++ */ ++int FontConfig_OHOS::getErrorCount() const ++{ ++ return errSet.size(); ++} ++ ++/*! To sort the typeface set ++ * \param typefaceSet the typeface set to be sorted ++ */ ++void FontConfig_OHOS::sortTypefaceSet(std::shared_ptr& typefaceSet) ++{ ++ if (typefaceSet.get() == nullptr || typefaceSet->size() <= 1) { ++ return; ++ } ++ TypefaceSet& tpSet = *(typefaceSet.get()); ++ for (unsigned int i = 0; i < tpSet.size(); i++) ++ for (unsigned int j = 0; j < tpSet.size() - 1; j++) { ++ if ((tpSet[j]->fontStyle().weight() > tpSet[j + 1]->fontStyle().weight()) || ++ (tpSet[j]->fontStyle().weight() == tpSet[j + 1]->fontStyle().weight() && ++ tpSet[j]->fontStyle().slant() > tpSet[j + 1]->fontStyle().slant())) { ++ tpSet[j].swap(tpSet[j + 1]); ++ } ++ } ++} ++ ++/*! To get the display text of an error ++ * \param err the id of an error ++ * \return The text to explain the error ++ */ ++const char* FontConfig_OHOS::errToString(int err) ++{ ++ const static std::array errToString{ ++ "successful", // NO_ERROR = 0 ++ "config file is not found", // ERROR_CONFIG_NOT_FOUND ++ "the format of config file is not supported", // ERROR_CONFIG_FORMAT_NOT_SUPPORTED ++ "missing tag", // ERROR_CONFIG_MISSING_TAG ++ "invalid value type", // ERROR_CONFIG_INVALID_VALUE_TYPE ++ "font file is not exist", // ERROR_FONT_NOT_EXIST ++ "invalid font stream", // ERROR_FONT_INVALID_STREAM ++ "no font stream", // ERROR_FONT_NO_STREAM ++ "family is not found", // ERROR_FAMILY_NOT_FOUND ++ "no available family in the system", //ERROR_NO_AVAILABLE_FAMILY ++ "no such directory" // ERROR_DIR_NOT_FOUND ++ }; ++ if (err >= 0 && err < ERROR_TYPE_COUNT) { ++ return errToString[err]; ++ } ++ return "unknown error"; ++} ++ ++/*! To log the error information ++ * \param err the id of an error ++ * \param key the key which indicates the the part with the error ++ * \param expected the expected type of json node. ++ * \n It's used only for err 'ERROR_CONFIG_INVALID_VALUE_TYPE' ++ * \param actual the actual type of json node. ++ * \n It's used only for err 'ERROR_CONFIG_INVALID_VALUE_TYPE' ++ * \return err ++ */ ++int FontConfig_OHOS::logErrInfo(int err, const char* key, Json::ValueType expected, ++ Json::ValueType actual) ++{ ++ errSet.emplace_back(err, key); ++ if (err != ERROR_CONFIG_INVALID_VALUE_TYPE) { ++ LOGE("%s : %s\n", errToString(err), key); ++ } else { ++ const char* types[] = { ++ "null", ++ "int", ++ "unit", ++ "real", ++ "string", ++ "boolean", ++ "array", ++ "object", ++ }; ++ int size = sizeof(types) / sizeof(char*); ++ if ((expected >= 0 && expected < size) && ++ (actual >= 0 && actual < size)) { ++ LOGE("%s : '%s' should be '%s', but here it's '%s'\n", ++ errToString(err), key, types[expected], types[actual]); ++ } else { ++ LOGE("%s : %s\n", errToString(err), key); ++ } ++ } ++ return err; ++} ++ ++bool FontConfig_OHOS::judgeFileExist() ++{ ++ bool haveFile = false; ++ for (unsigned int i = 0; i < fontDirSet.size(); i++) { ++ DIR* dir = opendir(fontDirSet[i].c_str()); ++ if (dir == nullptr) { ++ logErrInfo(ERROR_DIR_NOT_FOUND, fontDirSet[i].c_str()); ++ continue; ++ } ++ struct dirent* node = nullptr; ++#if defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_WIN) ++ struct stat fileStat; ++#endif ++ while ((node = readdir(dir))) { ++#if defined(SK_BUILD_FONT_MGR_FOR_PREVIEW_WIN) ++ stat(node->d_name, &fileStat); ++ if (S_ISDIR(fileStat.st_mode)) { ++ continue; ++ } ++#else ++ if (node->d_type != DT_REG) { ++ continue; ++ } ++#endif ++ const char* fileName = node->d_name; ++ int len = strlen(fileName); ++ int suffixLen = strlen(".ttf"); ++ if (len < suffixLen || (strncmp(fileName + len - suffixLen, ".ttf", suffixLen) && ++ strncmp(fileName + len - suffixLen, ".otf", suffixLen) && ++ strncmp(fileName + len - suffixLen, ".ttc", suffixLen) && ++ strncmp(fileName + len - suffixLen, ".otc", suffixLen))) { ++ continue; ++ } ++ haveFile = true; ++ break; ++ } ++ (void)closedir(dir); ++ if (haveFile) { ++ break; ++ } ++ } ++ return haveFile; ++} ++ ++int FontConfig_OHOS::checkProductFile(const char* fname) ++{ ++ int err = parseConfig(PRODUCT_DEFAULT_CONFIG); ++ SkDebugf("parse productfontconfig json file err = %d", err); ++ if ((err != NO_ERROR) || (!judgeFileExist())) { ++ SkDebugf("parse productfontconfig json file error"); ++ fontDirSet.clear(); ++ fallbackForMap.reset(); ++ genericFamilySet.clear(); ++ fallbackSet.clear(); ++ genericNames.reset(); ++ fallbackNames.reset(); ++ errSet.clear(); ++ aliasMap.reset(); ++ adjustMap.reset(); ++ variationMap.reset(); ++ ttcIndexMap.reset(); ++ err = parseConfig(fname); ++ } ++ return err; ++} +diff --git a/src/ports/skia_ohos/FontConfig_ohos.h b/src/ports/skia_ohos/FontConfig_ohos.h +new file mode 100644 +index 0000000000..d23dc47c44 +--- /dev/null ++++ b/src/ports/skia_ohos/FontConfig_ohos.h +@@ -0,0 +1,234 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef FONTCONFIG_OHOS_H ++#define FONTCONFIG_OHOS_H ++ ++#include ++#include ++ ++#include "SkFontDescriptor.h" ++#include "SkFontHost_FreeType_common.h" ++#include "SkFontStyle.h" ++#include "SkStream.h" ++#include "SkString.h" ++#include "SkTypes.h" ++ ++#include "FontInfo_ohos.h" ++#include "SkTypeface_ohos.h" ++ ++#ifdef ENABLE_DEBUG ++ ++#define LOGE(fmt, args...) \ ++ printf("E %s:%d %s - " fmt, basename(__FILE__), __LINE__, __FUNCTION__, ##args) ++#define LOGI(fmt, args...) \ ++ printf("I %s:%d - " fmt, __FUNCTION__, __LINE__, ##args) ++#define LOGW(fmt, args...) \ ++ printf("W %s:%d %s - " fmt, basename(__FILE__), __LINE__, __FUNCTION__, ##args) ++ ++#else ++ ++#define LOGE SkDEBUGF ++#define LOGI SkDEBUGF ++#define LOGW SkDEBUGF ++ ++#endif ++ ++struct FontInfo; ++struct FallbackInfo; ++struct GenericFamily; ++struct FallbackSetPos; ++ ++using TypefaceSet = std::vector>; ++using GenericFamilySet = std::vector>; ++using FallbackSet = std::vector>; ++using FallbackForMap = SkTHashMap; ++using NamesMap = SkTHashMap; ++using Coordinate = SkFontArguments::VariationPosition::Coordinate; ++using AxisDefinitions = SkTypeface_FreeType::Scanner::AxisDefinitions; ++ ++/*! ++ * Error code definition ++ */ ++namespace ErrorCode { ++ ++enum { ++ NO_ERROR = 0, // no error ++ ERROR_CONFIG_NOT_FOUND, // the configuration document is not found ++ ERROR_CONFIG_FORMAT_NOT_SUPPORTED, // the formation of configuration is not supported ++ ERROR_CONFIG_MISSING_TAG, // missing tag in the configuration ++ ERROR_CONFIG_INVALID_VALUE_TYPE, // invalid value type in the configuration ++ ERROR_FONT_NOT_EXIST, // the font file is not exist ++ ERROR_FONT_INVALID_STREAM, // the stream is not recognized ++ ERROR_FONT_NO_STREAM, // no stream in the font data ++ ERROR_FAMILY_NOT_FOUND, // the family name is not found in the system ++ ERROR_NO_AVAILABLE_FAMILY, // no available family in the system ++ ERROR_DIR_NOT_FOUND, // the directory is not exist ++ ++ ERROR_TYPE_COUNT, ++}; ++ ++} /* namespace ErrorCode */ ++ ++/*! ++ * \brief To manage the related information of a 'fallbackFor' family name ++ */ ++struct FallbackSetPos { ++ unsigned int index; // the index of the first font style set in the fallback set for a specified family name ++ unsigned int count; // the count of font style sets for a specified family name ++}; ++ ++/*! ++ * \brief To manage the information for a generic family item ++ */ ++struct GenericFamily { ++ SkString familyName; // the specified family name of the font style set ++ std::shared_ptr typefaceSet; // the typeface set of the font style set ++ virtual ~GenericFamily() = default; ++}; ++ ++/*! ++ * \brief To manage the information for a fallback family item ++ */ ++struct FallbackInfo : GenericFamily { ++ SkString langs; // the language for which the font style set is ++}; ++ ++/*! ++ * \brief To parse the font configuration document and manage the system fonts ++ */ ++class FontConfig_OHOS { ++public: ++ explicit FontConfig_OHOS(const SkTypeface_FreeType::Scanner& fontScanner, ++ const char* fname = nullptr); ++ virtual ~FontConfig_OHOS() = default; ++ const FallbackForMap& getFallbackForMap() const; ++ const FallbackSet& getFallbackSet() const; ++ int getFamilyCount() const; ++ int getDefaultFamily(SkString* familyName) const; ++ int getFamilyName(int index, SkString* familyName) const; ++ int getTypefaceCount(int styleIndex, bool isFallback = false) const; ++ int getStyleIndex(const char* familyName, bool& isFallback) const; ++ ++ SkTypeface_OHOS* getTypeface(int styleIndex, int index, bool isFallback = false) const; ++ SkTypeface_OHOS* getTypeface(int styleIndex, const SkFontStyle& style, ++ bool isFallback = false) const; ++ ++#if ENABLE_DEBUG ++ void dumpFont(const FontInfo& font) const; ++ void dumpGeneric() const; ++ void dumpFallback() const; ++#endif ++ bool hasError(int err, const SkString& text) const; ++ int getErrorCount() const; ++ ++ static sk_sp matchFontStyle(const TypefaceSet& typefaceSet, const SkFontStyle& pattern); ++ ++ static const char* errToString(int err); ++private: ++ struct AliasInfo; ++ struct AdjustInfo; ++ struct VariationInfo; ++ struct TtcIndexInfo; ++ using AliasMap = SkTHashMap>; ++ using AjdustMap = SkTHashMap>; ++ using VariationMap = SkTHashMap>; ++ using TtcIndexMap = SkTHashMap; ++ ++ /*! ++ * \brief To manage the adjust information ++ */ ++ struct AdjustInfo { ++ int origValue; // the real value of the font weight ++ int newValue; // the specified value of weight for a font ++ }; ++ ++ /*! ++ * \brief To manage the alias information of ++ */ ++ struct AliasInfo { ++ int pos; // the index of a font style set in generic family list. ++ int weight; // the weight of the font style set. 0 means no specified weight ++ }; ++ ++ /*! ++ * \brief To manage the variation information ++ */ ++ struct VariationInfo { ++ VariationInfo() : weight(-1), width(-1), slant(-1){} ++ std::vector axis; // the axis set such as 'wght', 'wdth' and 'slnt'. ++ int weight; // the value of mapping weight ++ int width; // the value of mapping width ++ int slant; // the value of mapping slant ++ }; ++ ++ /*! ++ * \brief To manage the 'index' information for ttc fonts ++ */ ++ struct TtcIndexInfo { ++ SkString familyName; // the family name of the first typeface in a ttc font ++ int ttcIndex; // the index of a typeface in a ttc font ++ }; ++ ++ /*! ++ * \brief To manage the information of errors happened ++ */ ++ struct ErrorInfo { ++ ErrorInfo(int err, const char* text) : err(err), text(SkString(text)){} ++ ErrorInfo(int err, SkString& text) : err(err), text(std::move(text)){} ++ int err; // error id ++ SkString text; // the part with error ++ }; ++ ++ std::vector fontDirSet; // the directories where the fonts are ++ ++ FallbackForMap fallbackForMap; // a hash table to save the fallbackFor pairs ++ GenericFamilySet genericFamilySet; // the font style set list of generic family ++ FallbackSet fallbackSet; // the font style set list of fallback family ++ ++ NamesMap genericNames; // a map to store the index of a family for generic family ++ NamesMap fallbackNames; // a map to store the index of a family for fallback family ++ ++ std::vector errSet; // the errors happened ++ AliasMap aliasMap; // to save alias information temporarily ++ AjdustMap adjustMap; // to save adjust information temporarily ++ VariationMap variationMap; // to save variation information temporarily ++ TtcIndexMap ttcIndexMap; // to save 'index' information temporarily ++ ++ int parseConfig(const char* fname); ++ int checkConfigFile(const char* fname, Json::Value& root); ++ int parseFontDir(const Json::Value& root); ++ int parseGeneric(const Json::Value& root); ++ int parseFallback(const Json::Value& root); ++ int parseFallbackItem(const Json::Value& root); ++ int parseAlias(const Json::Value& root, std::vector& aliasSet); ++ int parseAdjust(const Json::Value& root, std::vector& adjustSet); ++ int parseVariation(const Json::Value& root, std::vector& variationSet); ++ int parseTtcIndex(const Json::Value& root, const SkString& familyName); ++ void getAxisValues(const AxisDefinitions& axisDefinitions, ++ const VariationInfo& variation, FontInfo& font) const; ++ bool insertTtcFont(int count, FontInfo& font); ++ bool insertVariableFont(const AxisDefinitions& axisDefinitions, FontInfo& font); ++ TypefaceSet* getTypefaceSet(const SkString& familyName, SkString& specifiedName) const; ++ ++ int loadFont(const SkTypeface_FreeType::Scanner& scanner, const char* fname); ++ int scanFonts(const SkTypeface_FreeType::Scanner& fontScanner); ++ void resetGenericValue(); ++ void buildSubTypefaceSet(const std::shared_ptr& typefaceSet, ++ std::shared_ptr& subSet, const SkString& familyName, int weight); ++ void resetFallbackValue(); ++ int logErrInfo(int err, const char* key, Json::ValueType expected = Json::nullValue, ++ Json::ValueType actual = Json::nullValue); ++ static void sortTypefaceSet(std::shared_ptr& typefaceSet); ++ static uint32_t getFontStyleDifference(const SkFontStyle& style1, const SkFontStyle& style2); ++ static char* getFileData(const char* fname, int& size); ++ FontConfig_OHOS(const FontConfig_OHOS&) = delete; ++ FontConfig_OHOS& operator = (const FontConfig_OHOS&) = delete; ++ FontConfig_OHOS(FontConfig_OHOS&&) = delete; ++ FontConfig_OHOS& operator = (FontConfig_OHOS&&) = delete; ++ int checkProductFile(const char* fname); ++ bool judgeFileExist(); ++}; ++ ++#endif /* FONTCONFIG_OHOS_H */ +diff --git a/src/ports/skia_ohos/FontInfo_ohos.h b/src/ports/skia_ohos/FontInfo_ohos.h +new file mode 100644 +index 0000000000..0b851ae3fa +--- /dev/null ++++ b/src/ports/skia_ohos/FontInfo_ohos.h +@@ -0,0 +1,145 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef FONTINFO_OHOS_H ++#define FONTINFO_OHOS_H ++ ++#include "SkFixed.h" ++#include "SkFontDescriptor.h" ++#include "SkFontHost_FreeType_common.h" ++ ++/*! ++ * \brief To manage the font information ++ */ ++struct FontInfo { ++public: ++ /*! Constructor ++ * ++ */ ++ FontInfo() : familyName(""), fname(""), index(0), ++ style(SkFontStyle::Normal()), isFixedWidth(false), stream(nullptr) ++ { ++ memset(&axisSet, 0, sizeof(AxisSet)); ++ } ++ /*! Copy Constructor ++ * \param font an object of FontInfo ++ */ ++ explicit FontInfo(const FontInfo& font) ++ : familyName(font.familyName), fname(font.fname), index(font.index), ++ style(font.style), isFixedWidth(font.isFixedWidth), stream(nullptr) ++ { ++ axisSet.axis = font.axisSet.axis; ++ axisSet.range = font.axisSet.range; ++ if (font.stream) { ++ stream = font.stream->duplicate(); ++ } ++ } ++ ++ /*! Move Constructor ++ * \param font an object of FontInfo ++ */ ++ explicit FontInfo(FontInfo&& font) ++ : familyName(std::move(font.familyName)), fname(std::move(font.fname)), index(font.index), ++ style(font.style), isFixedWidth(font.isFixedWidth), stream(nullptr) ++ { ++ axisSet.axis = std::move(font.axisSet.axis); ++ axisSet.range = std::move(font.axisSet.range); ++ if (font.stream) { ++ stream = std::move(font.stream); ++ } ++ } ++ ++ /*! Constructor ++ * \param fname the fullname of font file ++ * \param index the index of the typeface in the font file ++ */ ++ FontInfo(const char* fname, int index) ++ : familyName(""), fname(""), index(index), ++ style(SkFontStyle::Normal()), isFixedWidth(false), stream(nullptr) ++ { ++ if (fname) { ++ this->fname.set(fname); ++ } ++ memset(&axisSet, 0, sizeof(axisSet)); ++ } ++ ++ /*! Destructor ++ * ++ */ ++ virtual ~FontInfo() = default; ++ ++ /*! Copy assignment operator ++ * \param font an object of FontInfo ++ */ ++ FontInfo& operator = (const FontInfo& font) ++ { ++ if (this == &font) { ++ return *this; ++ } ++ familyName = font.familyName; ++ fname = font.fname; ++ index = font.index; ++ style = font.style; ++ isFixedWidth = font.isFixedWidth; ++ axisSet.axis = font.axisSet.axis; ++ axisSet.range = font.axisSet.range; ++ if (font.stream) { ++ stream = font.stream->duplicate(); ++ } ++ return *this; ++ } ++ ++ /*! The move assignment operator ++ * \param font an object of FontInfo ++ */ ++ FontInfo& operator = (FontInfo&& font) ++ { ++ if (this == &font) { ++ return *this; ++ } ++ familyName = std::move(font.familyName); ++ fname = std::move(font.fname); ++ index = font.index; ++ style = font.style; ++ isFixedWidth = font.isFixedWidth; ++ axisSet.axis = std::move(font.axisSet.axis); ++ axisSet.range = std::move(font.axisSet.range); ++ if (font.stream) { ++ stream = std::move(font.stream); ++ } ++ return *this; ++ } ++ ++ /*! To set axis values ++ * \param count the count of axis ++ * \param axis an array of SkFixed value ++ * \param range an array of AxisDefinition ++ */ ++ void setAxisSet(int count, const SkFixed* axis, ++ const SkTypeface_FreeType::Scanner::AxisDefinition* range) ++ { ++ axisSet.axis.clear(); ++ axisSet.range.clear(); ++ for (int i = 0; i < count; i++) { ++ axisSet.axis.emplace_back(axis[i]); ++ axisSet.range.emplace_back(range[i]); ++ } ++ } ++ ++ SkString familyName; // the real family name of the font ++ SkString fname; // the full name of font file ++ int index; // the index of the font in a ttc font ++ SkFontStyle style; // the font style ++ bool isFixedWidth; // the flag to indicate if the font has fixed width or not ++ /*! ++ * \brief To manage the axis values for variable font ++ */ ++ struct AxisSet { ++ std::vector axis; // the axis values ++ std::vector range; // the axis ranges ++ } axisSet; // the axis values for a variable font ++ std::unique_ptr stream; // the data stream of font file ++}; ++ ++#endif /* FONTINFO_OHOS_H */ +diff --git a/src/ports/skia_ohos/SkFontMgr_ohos.cpp b/src/ports/skia_ohos/SkFontMgr_ohos.cpp +new file mode 100644 +index 0000000000..029d105ebc +--- /dev/null ++++ b/src/ports/skia_ohos/SkFontMgr_ohos.cpp +@@ -0,0 +1,437 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "SkFontMgr_ohos.h" ++ ++#include "SkTypeface_ohos.h" ++ ++using namespace ErrorCode; ++ ++/*! Constructor ++ * \param path the full path of system font configuration document ++ */ ++SkFontMgr_OHOS::SkFontMgr_OHOS(const char* path) ++{ ++ fontConfig = std::make_shared(fontScanner, path); ++ familyCount = fontConfig->getFamilyCount(); ++} ++ ++/*! To get the count of families ++ * \return The count of families in the system ++ */ ++int SkFontMgr_OHOS::onCountFamilies() const ++{ ++ return familyCount; ++} ++ ++/*! To get the family name for a font style set ++ * \param index the index of a font style set ++ * \param[out] familyName the family name returned to the caller ++ * \n The family name will be reset to "", if index is out of range ++ */ ++void SkFontMgr_OHOS::onGetFamilyName(int index, SkString* familyName) const ++{ ++ if (fontConfig == nullptr || familyName == nullptr) { ++ return; ++ } ++ fontConfig->getFamilyName(index, familyName); ++} ++ ++/*! To create an object of SkFontStyleSet ++ * \param index the index of a font style set ++ * \return The pointer of SkFontStyleSet ++ * \n Return null, if index is out of range ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++SkFontStyleSet* SkFontMgr_OHOS::onCreateStyleSet(int index) const ++{ ++ if (fontConfig == nullptr) { ++ return nullptr; ++ } ++ if (index < 0 || index >= this->countFamilies()) { ++ return nullptr; ++ } ++ return new SkFontStyleSet_OHOS(fontConfig, index); ++} ++ ++/*! To get a matched object of SkFontStyleSet ++ * \param familyName the family name of a font style set ++ * \return The pointer of SkFontStyleSet ++ * \n Return the default font style set, if family name is null ++ * \n Return null, if family name is not found ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++SkFontStyleSet* SkFontMgr_OHOS::onMatchFamily(const char familyName[]) const ++{ ++ if (fontConfig == nullptr) { ++ return nullptr; ++ } ++ // return default system font when familyName is null ++ if (familyName == nullptr) { ++ return new SkFontStyleSet_OHOS(fontConfig, 0); ++ } ++ ++ bool isFallback = false; ++ int index = fontConfig->getStyleIndex(familyName, isFallback); ++ if (index == -1) { ++ return nullptr; ++ } ++ return new SkFontStyleSet_OHOS(fontConfig, index, isFallback); ++} ++ ++/*! To get a matched typeface ++ * \param familyName the family name of a font style set ++ * \param style the font style to be matched ++ * \return An object of typeface which is closest matching to 'style' ++ * \n Return the typeface in the default font style set, if family name is null ++ * \n Return null, if family name is not found ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++SkTypeface* SkFontMgr_OHOS::onMatchFamilyStyle(const char familyName[], const SkFontStyle& style) const ++{ ++ if (fontConfig == nullptr) { ++ return nullptr; ++ } ++ bool isFallback = false; ++ int styleIndex = 0; ++ if (familyName) { ++ styleIndex = fontConfig->getStyleIndex(familyName, isFallback); ++ } ++ return SkSafeRef(fontConfig->getTypeface(styleIndex, style, isFallback)); ++} ++ ++/*! To get a matched typeface ++ * \n Use the system fallback to find a typeface for the given character. ++ * \param familyName the family name which the typeface is fallback For ++ * \param style the font style to be matched ++ * \param bcp47 an array of languages which indicate the language of 'character' ++ * \param bcp47Count the array size of bcp47 ++ * \param character a UTF8 value to be matched ++ * \return An object of typeface which is for the given character ++ * \return Return the typeface in the default fallback set, if familyName is null ++ * \return Return null, if the typeface is not found for the given character ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++SkTypeface* SkFontMgr_OHOS::onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle& style, ++ const char* bcp47[], int bcp47Count, SkUnichar character) const ++{ ++ if (fontConfig == nullptr) { ++ return nullptr; ++ } ++ const FallbackForMap& fallbackForMap = fontConfig->getFallbackForMap(); ++ const FallbackSet& fallbackSet = fontConfig->getFallbackSet(); ++ SkString defaultFamily(""); ++ SkString key = defaultFamily; ++ FallbackSetPos* item = nullptr; ++ if (familyName == nullptr) { ++ item = fallbackForMap.find(defaultFamily); ++ } else { ++ item = fallbackForMap.find(SkString(familyName)); ++ if (item) { ++ key = SkString(familyName); ++ } else { ++ item = fallbackForMap.find(defaultFamily); ++ } ++ } ++ if (item == nullptr) { ++ LOGE("%s : '%s' must be a fallback key in the config file\n", ++ FontConfig_OHOS::errToString(ERROR_FAMILY_NOT_FOUND), defaultFamily.c_str()); ++ return nullptr; ++ } ++ while (true) { ++ if (bcp47Count > 0) { ++ SkTypeface* retTp = findTypeface(*item, style, bcp47, bcp47Count, character); ++ if (retTp) { ++ return retTp; ++ } ++ if (key == defaultFamily) { ++ bcp47Count = 0; ++ continue; ++ } ++ item = fallbackForMap.find(defaultFamily); ++ key = defaultFamily; ++ } else { ++ for (unsigned int i = item->index; i < item->index + item->count && i < fallbackSet.size(); i++) { ++ const TypefaceSet& tpSet = *(fallbackSet[i]->typefaceSet.get()); ++ if (tpSet.size() > 0 && tpSet[0]->unicharToGlyph(character) != 0) { ++ sk_sp typeface = FontConfig_OHOS::matchFontStyle(tpSet, style); ++ return SkSafeRef(typeface.get()); ++ } ++ } ++ if (key == defaultFamily) { ++ break; ++ } ++ item = fallbackForMap.find(defaultFamily); ++ key = defaultFamily; ++ } ++ } ++ return nullptr; ++} ++ ++/*! To find the matched typeface for the given parameters ++ * \n Use the system fallback to find a typeface for the given character. ++ * \param fallbackItem the fallback items in which to find the typeface ++ * \param style the font style to be matched ++ * \param bcp47 an array of languages which indicate the language of 'character' ++ * \param bcp47Count the array size of bcp47 ++ * \param character a UTF8 value to be matched ++ * \return An object of typeface which is for the given character ++ * \return Return null, if the typeface is not found for the given character ++ */ ++SkTypeface* SkFontMgr_OHOS::findTypeface(const FallbackSetPos& fallbackItem, const SkFontStyle& style, ++ const char* bcp47[], int bcp47Count, SkUnichar character) const ++{ ++ if (bcp47Count == 0) { ++ return nullptr; ++ } ++ ++ const FallbackSet& fallbackSet = fontConfig->getFallbackSet(); ++ // example bcp47 code : 'zh-Hans' : ('zh' : iso639 code, 'Hans' : iso15924 code) ++ // iso639 code will be taken from bcp47 code, so that we can try to match ++ // bcp47 or only iso639. Therefore totalCount need to be 'bcp47Count * 2' ++ int totalCount = bcp47Count * 2; ++ int tps[totalCount]; ++ for (int i = 0; i < totalCount; i++) { ++ tps[i] = -1; ++ } ++ // find the families matching the bcp47 list ++ for (unsigned int i = fallbackItem.index; i < fallbackItem.index + fallbackItem.count ++ && i < fallbackSet.size(); i++) { ++ int ret = compareLangs(fallbackSet[i]->langs, bcp47, bcp47Count, tps); ++ if (ret == -1) { ++ continue; ++ } ++ tps[ret] = i; ++ } ++ // match typeface in families ++ for (int i = bcp47Count - 1; i >= 0; i--) { ++ if (tps[i] == -1) { ++ continue; ++ } ++ const TypefaceSet& tpSet = *(fallbackSet[tps[i]]->typefaceSet.get()); ++ if (tpSet.size() > 0 && tpSet[0]->unicharToGlyph(character) != 0) { ++ sk_sp typeface = FontConfig_OHOS::matchFontStyle(tpSet, style); ++ return SkSafeRef(typeface.get()); ++ } ++ } ++ for (int i = totalCount - 1; i >= bcp47Count; i--) { ++ if (tps[i] == -1) { ++ continue; ++ } ++ const TypefaceSet& tpSet = *(fallbackSet[tps[i]]->typefaceSet.get()); ++ if (tpSet.size() > 0 && tpSet[0]->unicharToGlyph(character) != 0) { ++ sk_sp typeface = FontConfig_OHOS::matchFontStyle(tpSet, style); ++ return SkSafeRef(typeface.get()); ++ } ++ } ++ return nullptr; ++} ++ ++/*! To compare the languages of an typeface with a bcp47 list ++ * \param langs the supported languages by an typeface ++ * \param bcp47 the array of bcp47 language to be matching ++ * \param bcp47Count the array size of bcp47 ++ * \param tps an array of the index of typeface which is matching one value of bcp47 ++ * \return The index of language in bcp47, if matching happens ++ * \n Return -1, if no language matching happens ++ */ ++int SkFontMgr_OHOS::compareLangs(const SkString& langs, const char* bcp47[], ++ int bcp47Count, const int tps[]) const ++{ ++ /* ++ * zh-Hans : ('zh' : iso639 code, 'Hans' : iso15924 code) ++ */ ++ if (bcp47 == nullptr || bcp47Count == 0) { ++ return -1; ++ } ++ for (int i = bcp47Count - 1; i >= 0; i--) { ++ if (tps[i] != -1) { ++ continue; ++ } ++ if (langs.find(bcp47[i]) != -1) { ++ return i; ++ } else { ++ const char* iso15924 = strrchr(bcp47[i], '-'); ++ if (iso15924 == nullptr) { ++ continue; ++ } ++ iso15924++; ++ int len = iso15924 - 1 - bcp47[i]; ++ SkString country(bcp47[i], len); ++ if (langs.find(iso15924) != -1 || ++ (strncmp(bcp47[i], "und", strlen("und")) && langs.find(country.c_str()) != -1)) { ++ return i + bcp47Count; ++ } ++ } ++ } ++ return -1; ++} ++ ++/*! To get a matched typeface ++ * \param typeface the given typeface with which the returned object should be in the same style set ++ * \param style the font style to be matching ++ * \return The object of typeface which is closest matching to the given 'style' ++ * \n Return null, if the family name of the given typeface is not found in the system ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++SkTypeface* SkFontMgr_OHOS::onMatchFaceStyle(const SkTypeface* typeface, const SkFontStyle& style) const ++{ ++ if (typeface == nullptr) { ++ return nullptr; ++ } ++ SkString familyName; ++ typeface->getFamilyName(&familyName); ++ return this->onMatchFamilyStyle(familyName.c_str(), style); ++} ++ ++/*! To create a typeface from the specified data and TTC index ++ * \param data the data to be parsed ++ * \param index the index of typeface. 0 for none ++ * \return The object of typeface, if successful ++ * \n Return null if the data is not recognized. ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++sk_sp SkFontMgr_OHOS::onMakeFromData(sk_sp data, int ttcIndex) const ++{ ++ if (data == nullptr) { ++ return nullptr; ++ } ++ std::unique_ptr memoryStream = std::make_unique(data); ++ SkFontArguments args; ++ args.setCollectionIndex(ttcIndex); ++ return this->makeTypeface(std::move(memoryStream), args, nullptr); ++} ++ ++/*! To create a typeface from the specified stream and TTC index ++ * \param data the stream to be parsed ++ * \param index the index of typeface. 0 for none ++ * \return The object of typeface, if successful ++ * \n Return null if the stream is not recognized. ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++sk_sp SkFontMgr_OHOS::onMakeFromStreamIndex(std::unique_ptr stream, ++ int ttcIndex) const ++{ ++ if (stream == nullptr) { ++ return nullptr; ++ } ++ SkFontArguments args; ++ args.setCollectionIndex(ttcIndex); ++ return this->makeTypeface(std::move(stream), args, nullptr); ++} ++ ++/*! To create a typeface from the specified stream and font arguments ++ * \param data the stream to be parsed ++ * \param args the arguments of font ++ * \return The object of typeface, if successful ++ * \n Return null if the stream is not recognized. ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++sk_sp SkFontMgr_OHOS::onMakeFromStreamArgs(std::unique_ptr stream, ++ const SkFontArguments& args) const ++{ ++ if (stream == nullptr) { ++ return nullptr; ++ } ++ ++ return this->makeTypeface(std::move(stream), args, nullptr); ++} ++ ++/*! To create a typeface from the specified font file and TTC index ++ * \param path the full path of the given font file ++ * \param ttcIndex the index of typeface in a ttc font file. 0 means none. ++ * \return The object of typeface, if successful ++ * \n Return null if the font file is not found or the content of file is not recognized. ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++sk_sp SkFontMgr_OHOS::onMakeFromFile(const char path[], int ttcIndex) const ++{ ++ if (fontConfig == nullptr) { ++ return nullptr; ++ } ++ ++ std::unique_ptr stream = SkStreamAsset::MakeFromFile(path); ++ if (stream == nullptr) { ++ LOGE("%s : %s\n", FontConfig_OHOS::errToString(ERROR_FONT_NOT_EXIST), path); ++ return nullptr; ++ } ++ SkFontArguments args; ++ args.setCollectionIndex(ttcIndex); ++ return this->makeTypeface(std::move(stream), args, path); ++} ++ ++/*! To get a typeface matching the specified family and style ++ * \param familyName the specified name to be matching ++ * \param style the specified style to be matching ++ * \return The object of typeface which is the closest matching 'style' when the familyName is found ++ * \return Return a typeface from the default family, if familyName is not found ++ * \return Return null, if there is no any typeface in the system ++ * \note The caller must caller unref() on the returned object is it's not null ++ */ ++sk_sp SkFontMgr_OHOS::onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const ++{ ++ SkTypeface* typeface = this->onMatchFamilyStyle(familyName, style); ++ // if familyName is not found, then try the default family ++ if (typeface == nullptr && familyName != nullptr) { ++ typeface = this->onMatchFamilyStyle(nullptr, style); ++ } ++ ++ if (typeface) { ++ return sk_sp(typeface); ++ } ++ LOGE("%s\n", FontConfig_OHOS::errToString(ERROR_NO_AVAILABLE_FAMILY)); ++ return nullptr; ++} ++ ++/*! To make a typeface from the specified stream and font arguments ++ * \param stream the specified stream to be parsed to get font information ++ * \param args the arguments of index or axis values ++ * \param path the fullname of font file ++ * \return The object of typeface if successful ++ * \n Return null, if the stream is not recognized ++ */ ++sk_sp SkFontMgr_OHOS::makeTypeface(std::unique_ptr stream, ++ const SkFontArguments& args, const char path[]) const ++{ ++ FontInfo fontInfo; ++ int ttcIndex = args.getCollectionIndex(); ++ int axisCount = args.getVariationDesignPosition().coordinateCount; ++ ++ if (path) { ++ fontInfo.fname.set(path); ++ } ++ if (axisCount == 0) { ++ if (!fontScanner.scanFont(stream.get(), ttcIndex, &fontInfo.familyName, &fontInfo.style, ++ &fontInfo.isFixedWidth, nullptr)) { ++ LOGE("%s\n", FontConfig_OHOS::errToString(ERROR_FONT_INVALID_STREAM)); ++ return nullptr; ++ } ++ } else { ++ AxisDefinitions axisDef; ++ if (!fontScanner.scanFont(stream.get(), ttcIndex, &fontInfo.familyName, &fontInfo.style, ++ &fontInfo.isFixedWidth, &axisDef)) { ++ LOGE("%s\n", FontConfig_OHOS::errToString(ERROR_FONT_INVALID_STREAM)); ++ return nullptr; ++ } ++ if (axisDef.size() > 0) { ++ SkFixed axis[axisCount]; ++ fontScanner.computeAxisValues(axisDef, args.getVariationDesignPosition(), ++ axis, fontInfo.familyName); ++ fontInfo.setAxisSet(axisCount, axis, axisDef.data()); ++ } ++ } ++ ++ fontInfo.stream = std::move(stream); ++ fontInfo.index = ttcIndex; ++ return sk_make_sp(fontInfo); ++} ++ ++/*! To create SkFontMgr object for Harmony platform ++ * \param fname the full name of system font configuration documents ++ * \return The object of SkFontMgr_OHOS ++ */ ++sk_sp SkFontMgr_New_OHOS(const char* fname) ++{ ++ return sk_make_sp(fname); ++} +diff --git a/src/ports/skia_ohos/SkFontMgr_ohos.h b/src/ports/skia_ohos/SkFontMgr_ohos.h +new file mode 100644 +index 0000000000..b9b42ea204 +--- /dev/null ++++ b/src/ports/skia_ohos/SkFontMgr_ohos.h +@@ -0,0 +1,69 @@ ++/* ++ * Copyright 2015 Google Inc. ++ * ++ * Use of this source code is governed by a BSD-style license that can be ++ * found in the LICENSE file. ++ * 2023.4.23 SkFontMgr on ohos. ++ * Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved. ++ */ ++ ++#ifndef SKFONTMGR_OHOS_H ++#define SKFONTMGR_OHOS_H ++ ++#include "SkFontDescriptor.h" ++#include "SkFontMgr.h" ++ ++#include "FontConfig_ohos.h" ++#include "SkFontStyleSet_ohos.h" ++ ++/*! ++ * \brief To implement the SkFontMgr for ohos platform ++ */ ++class SkFontMgr_OHOS : public SkFontMgr { ++public: ++ explicit SkFontMgr_OHOS(const char* path = nullptr); ++ virtual ~SkFontMgr_OHOS() override = default; ++protected: ++ virtual int onCountFamilies() const override; ++ virtual void onGetFamilyName(int index, SkString* familyName) const override; ++ virtual SkFontStyleSet* onCreateStyleSet(int index)const override; ++ ++ virtual SkFontStyleSet* onMatchFamily(const char familyName[]) const override; ++ ++ virtual SkTypeface* onMatchFamilyStyle(const char familyName[], ++ const SkFontStyle& style) const override; ++ virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle& style, ++ const char* bcp47[], int bcp47Count, ++ SkUnichar character) const override; ++ ++ virtual SkTypeface* onMatchFaceStyle(const SkTypeface* typeface, ++ const SkFontStyle& style) const override; ++ ++ virtual sk_sp onMakeFromData(sk_sp data, int ttcIndex) const override; ++ virtual sk_sp onMakeFromStreamIndex(std::unique_ptr stream, ++ int ttcIndex) const override; ++ virtual sk_sp onMakeFromStreamArgs(std::unique_ptr stream, ++ const SkFontArguments& args) const override; ++ virtual sk_sp onMakeFromFile(const char path[], int ttcIndex) const override; ++ ++ virtual sk_sp onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override; ++ ++private: ++ std::shared_ptr fontConfig = nullptr; // the pointer of FontConfig_OHOS ++ SkTypeface_FreeType::Scanner fontScanner; // the scanner to parse a font file ++ int familyCount = 0; // the count of font style sets in generic family list ++ ++ int compareLangs(const SkString& langs, const char* bcp47[], int bcp47Count, const int tps[]) const; ++ sk_sp makeTypeface(std::unique_ptr stream, ++ const SkFontArguments& args, const char path[]) const; ++ SkTypeface* findTypeface(const FallbackSetPos& fallbackItem, const SkFontStyle& style, ++ const char* bcp47[], int bcp47Count, ++ SkUnichar character) const; ++}; ++ ++SK_API sk_sp SkFontMgr_New_OHOS(const char* path); ++SK_API sk_sp SkFontMgr_New_OHOS() { ++ return SkFontMgr_New_OHOS("/system/etc/fontconfig.json"); ++} ++ ++#endif /* SKFONTMGR_OHOS_H */ +diff --git a/src/ports/skia_ohos/SkFontMgr_ohos_factory.cpp b/src/ports/skia_ohos/SkFontMgr_ohos_factory.cpp +new file mode 100644 +index 0000000000..b975ceddd4 +--- /dev/null ++++ b/src/ports/skia_ohos/SkFontMgr_ohos_factory.cpp +@@ -0,0 +1,14 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "include/core/SkFontMgr.h" ++SK_API sk_sp SkFontMgr_New_OHOS(const char* path); ++ ++/*! To implement the porting layer to return the default factory for Harmony platform ++ * \return the default font manager for Harmony platform ++ */ ++sk_sp SkFontMgr::Factory() ++{ ++ return SkFontMgr_New_OHOS(nullptr); ++} +diff --git a/src/ports/skia_ohos/SkFontStyleSet_ohos.cpp b/src/ports/skia_ohos/SkFontStyleSet_ohos.cpp +new file mode 100644 +index 0000000000..094e3ee532 +--- /dev/null ++++ b/src/ports/skia_ohos/SkFontStyleSet_ohos.cpp +@@ -0,0 +1,102 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "SkFontStyleSet_ohos.h" ++ ++/*! Constructor ++ * \param fontConfig the pointer of FontConfig_OHOS ++ * \param index the index of the font style set ++ * \param isFallback true - the font style is from fallback family ++ * \n false - the font style is from generic family ++ */ ++SkFontStyleSet_OHOS::SkFontStyleSet_OHOS(const std::shared_ptr& fontConfig, ++ int index, bool isFallback) ++ : fontConfig_(fontConfig), styleIndex(index), isFallback(isFallback) ++{ ++ if (fontConfig) { ++ tpCount = fontConfig_->getTypefaceCount(styleIndex, isFallback); ++ } ++} ++ ++/*! To get the count of typeface ++ * \return The count of typeface in this font style set ++ */ ++int SkFontStyleSet_OHOS::count() ++{ ++ return tpCount; ++} ++ ++/*! To get the font style for the specified typeface ++ * \param the index of a typeface ++ * \param[out] style the style value returned to the caller ++ * \param[out] the style name returned to the caller ++ */ ++void SkFontStyleSet_OHOS::getStyle(int index, SkFontStyle* style, SkString* styleName) ++{ ++ if (index < 0 || index >= this->count() || fontConfig_ == nullptr) { ++ return; ++ } ++ ++ SkTypeface* typeface = fontConfig_->getTypeface(styleIndex, index, isFallback); ++ if (typeface == nullptr) { ++ return; ++ } ++ ++ if (style) { ++ *style = typeface->fontStyle(); ++ } ++ if (styleName) { ++ const char* names[] = { ++ "invisible", ++ "thin", ++ "extralight", ++ "light", ++ "normal", ++ "medium", ++ "semibold", ++ "bold", ++ "extrabold", ++ "black", ++ "extrablack" ++ }; ++ // the value of font weight is between 0 ~ 1000 (refer to SkFontStyle::Weight) ++ // the weight is divided by 100 to get the matched name ++ unsigned int i = typeface->fontStyle().weight() / 100; ++ if (i < sizeof(names) / sizeof(char*)) { ++ styleName->set(names[i]); ++ } else { ++ styleName->reset(); ++ } ++ } ++} ++ ++/*! To create a typeface ++ * \param index the index of the typeface in this font style set ++ * \return The object of a typeface, if successful ++ * \n Return null, if the 'index' is out of range ++ * \note The caller must call unref() on the returned object if it's not null ++ */ ++SkTypeface* SkFontStyleSet_OHOS::createTypeface(int index) ++{ ++ if (index < 0 || index >= this->count()) { ++ return nullptr; ++ } ++ if (fontConfig_) { ++ return SkSafeRef(fontConfig_->getTypeface(styleIndex, index, isFallback)); ++ } ++ return nullptr; ++} ++ ++/*! To get the closest matching typeface ++ * \param pattern the style value to be matching ++ * \return the object of a typeface which is the closest matching to 'pattern' ++ * \note The caller must call unref() on the returned object ++ */ ++SkTypeface* SkFontStyleSet_OHOS::matchStyle(const SkFontStyle& pattern) ++{ ++ if (fontConfig_) { ++ return SkSafeRef(fontConfig_->getTypeface(styleIndex, pattern, isFallback)); ++ } ++ return nullptr; ++} +diff --git a/src/ports/skia_ohos/SkFontStyleSet_ohos.h b/src/ports/skia_ohos/SkFontStyleSet_ohos.h +new file mode 100644 +index 0000000000..b9fb40cf1e +--- /dev/null ++++ b/src/ports/skia_ohos/SkFontStyleSet_ohos.h +@@ -0,0 +1,32 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef SKFONTSTYLESET_OHOS_H ++#define SKFONTSTYLESET_OHOS_H ++ ++#include "SkFontMgr.h" ++ ++#include "FontConfig_ohos.h" ++#include "SkTypeface_ohos.h" ++ ++/*! ++ * \brief To implement SkFontStyleSet for ohos platform ++ */ ++class SkFontStyleSet_OHOS : public SkFontStyleSet { ++public: ++ SkFontStyleSet_OHOS(const std::shared_ptr& fontConfig, ++ int index, bool isFallback = false); ++ virtual ~SkFontStyleSet_OHOS() override = default; ++ virtual int count() override; ++ virtual void getStyle(int index, SkFontStyle* style, SkString* styleName) override; ++ virtual SkTypeface* createTypeface(int index) override; ++ virtual SkTypeface* matchStyle(const SkFontStyle& pattern) override; ++private: ++ std::shared_ptr fontConfig_ = nullptr; // the object of FontConfig_OHOS ++ int styleIndex = 0; // the index of the font style set ++ bool isFallback = false; // the flag of font style set. False for fallback family, true for generic family. ++ int tpCount = -1; // the typeface count in the font style set ++}; ++ ++#endif /* SKFONTSTYLESET_OHOS_H */ +diff --git a/src/ports/skia_ohos/SkTypeface_ohos.cpp b/src/ports/skia_ohos/SkTypeface_ohos.cpp +new file mode 100644 +index 0000000000..3141944c32 +--- /dev/null ++++ b/src/ports/skia_ohos/SkTypeface_ohos.cpp +@@ -0,0 +1,139 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "SkTypeface_ohos.h" ++ ++#include "SkFontDescriptor.h" ++#include "SkFontHost_FreeType_common.h" ++#include "SkTArray.h" ++ ++/*! Constructor ++ * \param familyName the specified family name for the typeface ++ * \param info the font information for the typeface ++ */ ++SkTypeface_OHOS::SkTypeface_OHOS(const SkString& familyName, FontInfo& info) ++ : SkTypeface_FreeType(info.style, info.isFixedWidth), ++ specifiedName(familyName) ++{ ++ fontInfo = std::make_unique(std::move(info)); ++} ++ ++/*! Constructor ++ * \param info the font information for the typeface ++ */ ++SkTypeface_OHOS::SkTypeface_OHOS(FontInfo& info) ++ : SkTypeface_FreeType(info.style, info.isFixedWidth) ++{ ++ specifiedName.reset(); ++ fontInfo = std::make_unique(std::move(info)); ++} ++ ++/*! To get stream of the typeface ++ * \param[out] ttcIndex the index of the typeface in a ttc file returned to the caller ++ * \return The stream object of the typeface ++ */ ++std::unique_ptr SkTypeface_OHOS::onOpenStream(int* ttcIndex) const ++{ ++ if (fontInfo) { ++ if (ttcIndex) { ++ *ttcIndex = fontInfo->index; ++ } ++ if (fontInfo->stream == nullptr) { ++ fontInfo->stream = SkStream::MakeFromFile(fontInfo->fname.c_str()); ++ } ++ if (fontInfo->stream) { ++ return fontInfo->stream->duplicate(); ++ } ++ } ++ return nullptr; ++} ++ ++/*! To make font data from the typeface ++ * \return The object of SkFontData ++ */ ++std::unique_ptr SkTypeface_OHOS::onMakeFontData() const ++{ ++ if (fontInfo == nullptr) { ++ return nullptr; ++ } ++ ++ if (fontInfo->stream.get() == nullptr) { ++ fontInfo->stream = SkStream::MakeFromFile(fontInfo->fname.c_str()); ++ } ++ if (fontInfo->stream.get() == nullptr) { ++ return nullptr; ++ } ++ return std::make_unique(fontInfo->stream->duplicate(), fontInfo->index, 0, ++ fontInfo->axisSet.axis.data(), fontInfo->axisSet.axis.size(), nullptr, 0); ++} ++ ++/*! To get the font descriptor of the typeface ++ * \param[out] descriptor the font descriptor returned to the caller ++ * \param[out] isLocal the false to the caller ++ */ ++void SkTypeface_OHOS::onGetFontDescriptor(SkFontDescriptor* descriptor, bool* isLocal) const ++{ ++ if (isLocal) { ++ *isLocal = false; ++ } ++ if (descriptor) { ++ SkString familyName; ++ onGetFamilyName(&familyName); ++ descriptor->setFamilyName(familyName.c_str()); ++ descriptor->setStyle(this->fontStyle()); ++ } ++} ++ ++/*! To get the family name of the typeface ++ * \param[out] familyName the family name returned to the caller ++ */ ++void SkTypeface_OHOS::onGetFamilyName(SkString* familyName) const ++{ ++ if (familyName == nullptr) { ++ return; ++ } ++ if (specifiedName.size() > 0) { ++ *familyName = specifiedName; ++ } else { ++ if (fontInfo) { ++ *familyName = fontInfo->familyName; ++ } ++ } ++} ++ ++/*! To clone a typeface from this typeface ++ * \param args the specified font arguments from which the new typeface is created ++ * \return The object of a new typeface ++ * \note The caller must call unref() on the returned object ++ */ ++sk_sp SkTypeface_OHOS::onMakeClone(const SkFontArguments& args) const ++{ ++ FontInfo info(*(fontInfo.get())); ++ info.index = args.getCollectionIndex(); ++ unsigned int count = args.getVariationDesignPosition().coordinateCount; ++ if (count > 0 && count == fontInfo->axisSet.range.size()) { ++ SkFontArguments::VariationPosition position = args.getVariationDesignPosition(); ++ SkTypeface_FreeType::Scanner::AxisDefinitions axisDefs; ++ for (unsigned int i = 0; i < count; i++) { ++ axisDefs.push_back(fontInfo->axisSet.range[i]); ++ } ++ SkFixed axisValues[count]; ++ memset(axisValues, 0, sizeof(axisValues)); ++ SkTypeface_FreeType::Scanner::computeAxisValues(axisDefs, position, ++ axisValues, fontInfo->familyName); ++ info.axisSet.axis.clear(); ++ for (unsigned int i = 0; i < count; i++) { ++ info.axisSet.axis.emplace_back(axisValues[i]); ++ } ++ } ++ return sk_make_sp(specifiedName, info); ++} ++ ++/*! To get the font information of the typeface ++ * \return The object of FontInfo ++ */ ++const FontInfo* SkTypeface_OHOS::getFontInfo() const ++{ ++ return fontInfo.get(); ++} +diff --git a/src/ports/skia_ohos/SkTypeface_ohos.h b/src/ports/skia_ohos/SkTypeface_ohos.h +new file mode 100644 +index 0000000000..f80e920de0 +--- /dev/null ++++ b/src/ports/skia_ohos/SkTypeface_ohos.h +@@ -0,0 +1,34 @@ ++// Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef SKTYPEFACE_OHOS_H ++#define SKTYPEFACE_OHOS_H ++ ++#include "SkFontHost_FreeType_common.h" ++#include "SkFontStyle.h" ++#include "SkStream.h" ++ ++#include "FontInfo_ohos.h" ++ ++/*! ++ * \brief The implementation of SkTypeface for ohos platform ++ */ ++class SkTypeface_OHOS : public SkTypeface_FreeType { ++public: ++ SkTypeface_OHOS(const SkString& specifiedName, FontInfo& info); ++ explicit SkTypeface_OHOS(FontInfo& info); ++ virtual ~SkTypeface_OHOS() override = default; ++ const FontInfo* getFontInfo() const; ++protected: ++ virtual std::unique_ptr onOpenStream(int* ttcIndex) const override; ++ virtual std::unique_ptr onMakeFontData() const override; ++ virtual void onGetFontDescriptor(SkFontDescriptor* descriptor, bool* isLocal) const override; ++ virtual void onGetFamilyName(SkString* familyName) const override; ++ virtual sk_sp onMakeClone(const SkFontArguments& args) const override; ++private: ++ SkString specifiedName; // specified family name which is defined in the configuration file ++ std::unique_ptr fontInfo; // the font information of this typeface ++}; ++ ++#endif /* SKTYPEFACE_OHOS_H */ +diff --git a/src/ports/skia_ohos/config/fontconfig.json b/src/ports/skia_ohos/config/fontconfig.json +new file mode 100644 +index 0000000000..ec225a188e +--- /dev/null ++++ b/src/ports/skia_ohos/config/fontconfig.json +@@ -0,0 +1,76 @@ ++{ ++ "fontdir": ["/system/fonts/"], ++ "generic": [ ++ { ++ "family": "HarmonyOS Sans", ++ "alias": [ ++ { ++ "HarmonyOS-Sans": 0 ++ }, ++ { ++ "HarmonyOS-Sans-Light": 100 ++ }, ++ { ++ "HarmonyOS-Sans-Regular": 400 ++ }, ++ { ++ "HarmonyOS-Sans-Medium": 700 ++ }, ++ { ++ "HarmonyOS-Sans-Bold": 900 ++ } ++ ], ++ "adjust": [ ++ { ++ "weight": 50, "to": 100 ++ }, ++ { ++ "weight": 80, "to": 400 ++ }, ++ { ++ "weight": 100, "to": 700 ++ }, ++ { ++ "weight": 200, "to": 900 ++ } ++ ] ++ }, ++ { ++ "family": "HarmonyOS Sans Condensed", ++ "alias": [ ++ { ++ "HarmonyOS-Sans-Condensed": 0 ++ } ++ ] ++ }, ++ { ++ "family": "HarmonyOS Sans Digit", ++ "alias": [ ++ { ++ "HarmonyOS-Sans-Digit": 0 ++ } ++ ] ++ } ++ ], ++ "fallback": [ ++ { ++ "": [ ++ { ++ "zh-Hans": "HarmonyOS Sans SC" ++ }, ++ { ++ "zh-Hant": "HarmonyOS Sans TC" ++ }, ++ { ++ "und-Arab": "HarmonyOS Sans Naskh Arabic UI" ++ }, ++ { ++ "ja": "Noto Sans JP" ++ }, ++ { ++ "ko": "Noto Sans KR" ++ } ++ ] ++ } ++ ] ++} diff --git a/attachment/repos/spirv-headers.patch b/attachment/repos/spirv-headers.patch new file mode 100644 index 0000000000000000000000000000000000000000..717adf4554230d526a767f7e563c457f0a1ee854 --- /dev/null +++ b/attachment/repos/spirv-headers.patch @@ -0,0 +1,16 @@ +diff --git a/tools/buildHeaders/jsoncpp/dist/jsoncpp.cpp b/tools/buildHeaders/jsoncpp/dist/jsoncpp.cpp +index 1304914..7cdb50b 100644 +--- a/tools/buildHeaders/jsoncpp/dist/jsoncpp.cpp ++++ b/tools/buildHeaders/jsoncpp/dist/jsoncpp.cpp +@@ -2564,11 +2564,9 @@ LogicError::LogicError(std::string const& msg) + {} + void throwRuntimeError(std::string const& msg) + { +- throw RuntimeError(msg); + } + void throwLogicError(std::string const& msg) + { +- throw LogicError(msg); + } + + // ////////////////////////////////////////////////////////////////// diff --git a/attachment/repos/swiftshader-3.22.patch b/attachment/repos/swiftshader-3.22.patch new file mode 100644 index 0000000000000000000000000000000000000000..de852c2432ad561bc2ae7eea40e0b29a000f1865 --- /dev/null +++ b/attachment/repos/swiftshader-3.22.patch @@ -0,0 +1,39 @@ +diff --git a/src/Reactor/BUILD.gn b/src/Reactor/BUILD.gn +index 67dfeb0ec..a2b995813 100644 +--- a/src/Reactor/BUILD.gn ++++ b/src/Reactor/BUILD.gn +@@ -337,6 +337,8 @@ if (supports_llvm) { + include_dirs += [ "$llvm_dir/configs/android/include/" ] + } else if (is_mac) { + include_dirs += [ "$llvm_dir/configs/darwin/include/" ] ++ } else if (is_ohos) { ++ include_dirs += [ "$llvm_dir/configs/android/include/" ] + } else { + assert(false, "llvm not configured for target platform") + } +diff --git a/src/Reactor/reactor.gni b/src/Reactor/reactor.gni +index 04fad6f81..324003e35 100644 +--- a/src/Reactor/reactor.gni ++++ b/src/Reactor/reactor.gni +@@ -14,7 +14,7 @@ declare_args() { + } + + declare_args() { +- supports_llvm = is_linux || is_chromeos || is_fuchsia || is_win || is_android ++ supports_llvm = is_linux || is_chromeos || is_fuchsia || is_win || is_android || is_ohos + # LLVM uses C++17 features which require macOS 10.12, while Chrome's minimum platform for x86 is 10.11. + # Don't build LLVM on Mac, unless we have to. This only happens on ARM64 devices, which launched with 11.0. + # TODO(b/174843857): Remove check for !supports_subzero once Chrome supports macOS 10.12 +diff --git a/third_party/llvm-10.0/BUILD.gn b/third_party/llvm-10.0/BUILD.gn +index 59e52303c..9f59c7ff8 100644 +--- a/third_party/llvm-10.0/BUILD.gn ++++ b/third_party/llvm-10.0/BUILD.gn +@@ -108,6 +108,8 @@ if (is_linux || is_chromeos) { + llvm_include_dirs += [ "configs/windows/include/" ] + } else if (is_android) { + llvm_include_dirs += [ "configs/android/include/" ] ++} else if (is_ohos) { ++ llvm_include_dirs += [ "configs/android/include/" ] + } else if (is_mac) { + llvm_include_dirs += [ "configs/darwin/include/" ] + } else { diff --git a/attachment/repos/vulkan.patch b/attachment/repos/vulkan.patch new file mode 100644 index 0000000000000000000000000000000000000000..934724ad8a8137d097861b69019d102f336fb308 --- /dev/null +++ b/attachment/repos/vulkan.patch @@ -0,0 +1,1411 @@ +diff --git a/BUILD.gn b/BUILD.gn +index df1ba65..da302ab 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -24,6 +24,9 @@ config("vulkan_headers_config") { + if (is_android) { + defines += [ "VK_USE_PLATFORM_ANDROID_KHR" ] + } ++ if (is_ohos) { ++ defines += [ "VK_USE_PLATFORM_OHOS_KHR" ] ++ } + if (is_fuchsia) { + defines += [ "VK_USE_PLATFORM_FUCHSIA" ] + } +diff --git a/include/vulkan/vulkan.h b/include/vulkan/vulkan.h +index ef94006..5825970 100644 +--- a/include/vulkan/vulkan.h ++++ b/include/vulkan/vulkan.h +@@ -14,6 +14,10 @@ + #include "vulkan_android.h" + #endif + ++#ifdef VK_USE_PLATFORM_OHOS_KHR ++#include "vulkan_ohos.h" ++#endif ++ + #ifdef VK_USE_PLATFORM_FUCHSIA + #include + #include "vulkan_fuchsia.h" +diff --git a/include/vulkan/vulkan.hpp b/include/vulkan/vulkan.hpp +index c893919..671fe84 100644 +--- a/include/vulkan/vulkan.hpp ++++ b/include/vulkan/vulkan.hpp +@@ -14,6 +14,7 @@ + #include // std::string + #include + #include ++#include "vulkan/vulkan_core.h" + + #if 17 <= VULKAN_HPP_CPP_VERSION + # include +@@ -2526,6 +2527,13 @@ namespace VULKAN_HPP_NAMESPACE + return ::vkQueuePresentKHR( queue, pPresentInfo ); + } + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ VkResult vkQueueSignalReleaseImageOHOS( VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int32_t* pNativeFenceFd ) const VULKAN_HPP_NOEXCEPT ++ { ++ return ::vkQueueSignalReleaseImageOHOS( queue, waitSemaphoreCount, pWaitSemaphores, image, pNativeFenceFd); ++ } ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + VkResult vkGetDeviceGroupPresentCapabilitiesKHR( VkDevice device, + VkDeviceGroupPresentCapabilitiesKHR * pDeviceGroupPresentCapabilities ) const VULKAN_HPP_NOEXCEPT + { +@@ -3651,6 +3659,25 @@ namespace VULKAN_HPP_NAMESPACE + } + # endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++# if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ //=== VK_OHOS_external_memory === ++ ++ VkResult vkGetNativeBufferPropertiesOHOS( VkDevice device, ++ const struct OH_NativeBuffer* buffer, ++ VkNativeBufferPropertiesOHOS* pProperties ) const VULKAN_HPP_NOEXCEPT ++ { ++ return ::vkGetNativeBufferPropertiesOHOS( device, buffer, pProperties ); ++ } ++ ++ VkResult vkGetMemoryNativeBufferOHOS( VkDevice device, ++ const VkMemoryGetNativeBufferInfoOHOS * pInfo, ++ struct OH_NativeBuffer** pBuffer ) const VULKAN_HPP_NOEXCEPT ++ { ++ return ::vkGetMemoryNativeBufferOHOS( device, pInfo, pBuffer ); ++ } ++# endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ ++ + # if defined( VK_ENABLE_BETA_EXTENSIONS ) + //=== VK_AMDX_shader_enqueue === + +@@ -11380,6 +11407,28 @@ namespace VULKAN_HPP_NAMESPACE + }; + # endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++ ++# if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ template <> ++ struct StructExtends ++ { ++ enum ++ { ++ value = true ++ }; ++ }; ++ ++ template <> ++ struct StructExtends ++ { ++ enum ++ { ++ value = true ++ }; ++ }; ++# endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ ++ + # if defined( VK_ENABLE_BETA_EXTENSIONS ) + //=== VK_AMDX_shader_enqueue === + template <> +@@ -16907,6 +16956,10 @@ namespace VULKAN_HPP_NAMESPACE + PFN_vkGetPhysicalDevicePresentRectanglesKHR vkGetPhysicalDevicePresentRectanglesKHR = 0; + PFN_vkAcquireNextImage2KHR vkAcquireNextImage2KHR = 0; + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ PFN_vkQueueSignalReleaseImageOHOS vkQueueSignalReleaseImageOHOS = 0; ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + //=== VK_KHR_display === + PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR = 0; + PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR = 0; +@@ -17223,6 +17276,15 @@ namespace VULKAN_HPP_NAMESPACE + PFN_dummy vkGetMemoryAndroidHardwareBufferANDROID_placeholder = 0; + #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ //=== VK_OHOS_external_memory === ++ PFN_vkGetNativeBufferPropertiesOHOS vkGetNativeBufferPropertiesOHOS = 0; ++ PFN_vkGetMemoryNativeBufferOHOS vkGetMemoryNativeBufferOHOS = 0; ++#else ++ PFN_dummy vkGetNativeBufferPropertiesOHOS_placeholder = 0; ++ PFN_dummy vkGetMemoryNativeBufferOHOS_placeholder = 0; ++#endif /*VK_USE_PLATFORM_ANDROID_KHR*/ ++ + #if defined( VK_ENABLE_BETA_EXTENSIONS ) + //=== VK_AMDX_shader_enqueue === + PFN_vkCreateExecutionGraphPipelinesAMDX vkCreateExecutionGraphPipelinesAMDX = 0; +@@ -18120,6 +18182,10 @@ namespace VULKAN_HPP_NAMESPACE + PFN_vkGetPhysicalDevicePresentRectanglesKHR( vkGetInstanceProcAddr( instance, "vkGetPhysicalDevicePresentRectanglesKHR" ) ); + vkAcquireNextImage2KHR = PFN_vkAcquireNextImage2KHR( vkGetInstanceProcAddr( instance, "vkAcquireNextImage2KHR" ) ); + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ vkQueueSignalReleaseImageOHOS = PFN_vkQueueSignalReleaseImageOHOS( vkGetInstanceProcAddr( instance, "vkQueueSignalReleaseImageOHOS" )); ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + //=== VK_KHR_display === + vkGetPhysicalDeviceDisplayPropertiesKHR = + PFN_vkGetPhysicalDeviceDisplayPropertiesKHR( vkGetInstanceProcAddr( instance, "vkGetPhysicalDeviceDisplayPropertiesKHR" ) ); +@@ -18482,6 +18548,14 @@ namespace VULKAN_HPP_NAMESPACE + PFN_vkGetMemoryAndroidHardwareBufferANDROID( vkGetInstanceProcAddr( instance, "vkGetMemoryAndroidHardwareBufferANDROID" ) ); + #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ //=== VK_OHOS_external_memory === ++ vkGetNativeBufferPropertiesOHOS = ++ PFN_vkGetNativeBufferPropertiesOHOS( vkGetInstanceProcAddr( instance, "vkGetNativeBufferPropertiesOHOS" ) ); ++ vkGetMemoryNativeBufferOHOS = ++ PFN_vkGetMemoryNativeBufferOHOS( vkGetInstanceProcAddr( instance, "vkGetMemoryNativeBufferOHOS" ) ); ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + #if defined( VK_ENABLE_BETA_EXTENSIONS ) + //=== VK_AMDX_shader_enqueue === + vkCreateExecutionGraphPipelinesAMDX = PFN_vkCreateExecutionGraphPipelinesAMDX( vkGetInstanceProcAddr( instance, "vkCreateExecutionGraphPipelinesAMDX" ) ); +@@ -19411,6 +19485,10 @@ namespace VULKAN_HPP_NAMESPACE + PFN_vkGetDeviceGroupSurfacePresentModesKHR( vkGetDeviceProcAddr( device, "vkGetDeviceGroupSurfacePresentModesKHR" ) ); + vkAcquireNextImage2KHR = PFN_vkAcquireNextImage2KHR( vkGetDeviceProcAddr( device, "vkAcquireNextImage2KHR" ) ); + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ vkQueueSignalReleaseImageOHOS = PFN_vkQueueSignalReleaseImageOHOS( vkGetDeviceProcAddr( device, "vkQueueSignalReleaseImageOHOS" )); ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + //=== VK_KHR_display_swapchain === + vkCreateSharedSwapchainsKHR = PFN_vkCreateSharedSwapchainsKHR( vkGetDeviceProcAddr( device, "vkCreateSharedSwapchainsKHR" ) ); + +@@ -19607,6 +19685,14 @@ namespace VULKAN_HPP_NAMESPACE + PFN_vkGetMemoryAndroidHardwareBufferANDROID( vkGetDeviceProcAddr( device, "vkGetMemoryAndroidHardwareBufferANDROID" ) ); + #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ //=== VK_OHOS_external_memory === ++ vkGetNativeBufferPropertiesOHOS = ++ PFN_vkGetNativeBufferPropertiesOHOS( vkGetDeviceProcAddr( device, "vkGetNativeBufferPropertiesOHOS" ) ); ++ vkGetMemoryNativeBufferOHOS = ++ PFN_vkGetMemoryNativeBufferOHOS( vkGetDeviceProcAddr( device, "vkGetMemoryNativeBufferOHOS" ) ); ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + #if defined( VK_ENABLE_BETA_EXTENSIONS ) + //=== VK_AMDX_shader_enqueue === + vkCreateExecutionGraphPipelinesAMDX = PFN_vkCreateExecutionGraphPipelinesAMDX( vkGetDeviceProcAddr( device, "vkCreateExecutionGraphPipelinesAMDX" ) ); +diff --git a/include/vulkan/vulkan_core.h b/include/vulkan/vulkan_core.h +index a4666ed..c615598 100644 +--- a/include/vulkan/vulkan_core.h ++++ b/include/vulkan/vulkan_core.h +@@ -589,6 +589,16 @@ typedef enum VkStructureType { + VK_STRUCTURE_TYPE_MEMORY_GET_ANDROID_HARDWARE_BUFFER_INFO_ANDROID = 1000129004, + VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID = 1000129005, + VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_2_ANDROID = 1000129006, ++ VK_STRUCTURE_TYPE_SURFACE_CREATE_INFO_OHOS = 1000451000, ++ VK_STRUCTURE_TYPE_NATIVE_BUFFER_OHOS = 1000453001, ++ VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_OHOS = 1000453002, ++ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_OHOS = 1000453003, ++ VK_STRUCTURE_TYPE_NATIVE_BUFFER_USAGE_OHOS = 1000452000, ++ VK_STRUCTURE_TYPE_NATIVE_BUFFER_PROPERTIES_OHOS = 1000452001, ++ VK_STRUCTURE_TYPE_NATIVE_BUFFER_FORMAT_PROPERTIES_OHOS = 1000452002, ++ VK_STRUCTURE_TYPE_IMPORT_NATIVE_BUFFER_INFO_OHOS = 1000452003, ++ VK_STRUCTURE_TYPE_MEMORY_GET_NATIVE_BUFFER_INFO_OHOS = 1000452004, ++ VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_OHOS = 1000452005, + #ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ENQUEUE_FEATURES_AMDX = 1000134000, + #endif +@@ -4989,6 +4999,7 @@ typedef enum VkExternalMemoryHandleTypeFlagBits { + VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT = 0x00000100, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA = 0x00000800, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_RDMA_ADDRESS_BIT_NV = 0x00001000, ++ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OHOS_NATIVE_BUFFER_BIT_OHOS = 0x00002000, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_SCREEN_BUFFER_BIT_QNX = 0x00004000, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, +diff --git a/include/vulkan/vulkan_enums.hpp b/include/vulkan/vulkan_enums.hpp +index 547553a..84d40e4 100644 +--- a/include/vulkan/vulkan_enums.hpp ++++ b/include/vulkan/vulkan_enums.hpp +@@ -534,6 +534,9 @@ namespace VULKAN_HPP_NAMESPACE + #if defined( VK_USE_PLATFORM_WIN32_KHR ) + eWin32SurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, + #endif /*VK_USE_PLATFORM_WIN32_KHR*/ ++#if defined( VK_USE_PLATFORM_OHOS_KHR) ++ // eOHOSSurfaceCreateInfoKHR ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ + eDebugReportCallbackCreateInfoEXT = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, + eDebugReportCreateInfoEXT = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT, + ePipelineRasterizationStateRasterizationOrderAMD = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD, +@@ -780,6 +783,12 @@ namespace VULKAN_HPP_NAMESPACE + eExternalFormatANDROID = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID, + eAndroidHardwareBufferFormatProperties2ANDROID = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_2_ANDROID, + #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ ++#if defined( VK_USE_PLATFORM_OHOS_KHR) ++ eExternalFormatOHOS = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_OHOS, ++ eNativeBufferPropertiesOHOS = VK_STRUCTURE_TYPE_NATIVE_BUFFER_PROPERTIES_OHOS, ++ eMemoryGetNativeBufferInfoOHOS = VK_STRUCTURE_TYPE_MEMORY_GET_NATIVE_BUFFER_INFO_OHOS, ++ eNativeBufferFormatPropertiesOHOS = VK_STRUCTURE_TYPE_NATIVE_BUFFER_FORMAT_PROPERTIES_OHOS, ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ + ePhysicalDeviceSamplerFilterMinmaxPropertiesEXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT, + eSamplerReductionModeCreateInfoEXT = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT, + #if defined( VK_ENABLE_BETA_EXTENSIONS ) +diff --git a/include/vulkan/vulkan_funcs.hpp b/include/vulkan/vulkan_funcs.hpp +index 3c8d9ae..16de233 100644 +--- a/include/vulkan/vulkan_funcs.hpp ++++ b/include/vulkan/vulkan_funcs.hpp +@@ -8,6 +8,7 @@ + #ifndef VULKAN_FUNCS_HPP + #define VULKAN_FUNCS_HPP + ++#include "vulkan/vulkan_handles.hpp" + namespace VULKAN_HPP_NAMESPACE + { + +@@ -8811,6 +8812,34 @@ namespace VULKAN_HPP_NAMESPACE + } + #endif /* VULKAN_HPP_DISABLE_ENHANCED_MODE */ + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++template ++ VULKAN_HPP_NODISCARD VULKAN_HPP_INLINE VULKAN_HPP_NAMESPACE::Result Queue::queueSignalReleaseImageOHOS(int32_t waitSemaphoreCount, ++ VULKAN_HPP_NAMESPACE::Semaphore *pWaitSemaphores, VULKAN_HPP_NAMESPACE::Image pImage, int32_t* pNativeFenceFd, Dispatch const &d ) const ++ { ++ VULKAN_HPP_ASSERT( d.getVkHeaderVersion() == VK_HEADER_VERSION ); ++# if ( VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1 ) ++ VULKAN_HPP_ASSERT( d.vkQueueSignalReleaseImageOHOS && "Function requires " ); ++# endif ++ ++ VkSemaphore *semaphores; ++ semaphores = new VkSemaphore[waitSemaphoreCount]; ++ for (int i = 0; i < waitSemaphoreCount; i++) { ++ semaphores[i] = static_cast(pWaitSemaphores[i]); ++ } ++ ++ ++ VULKAN_HPP_NAMESPACE::Result result = ++ static_cast( d.vkQueueSignalReleaseImageOHOS( m_queue, waitSemaphoreCount, ++ semaphores, ++ static_cast( pImage ), ++ pNativeFenceFd)); ++ free(semaphores); ++ ++ return static_cast( result ); ++ } ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + template + VULKAN_HPP_NODISCARD VULKAN_HPP_INLINE Result Device::getGroupPresentCapabilitiesKHR( + VULKAN_HPP_NAMESPACE::DeviceGroupPresentCapabilitiesKHR * pDeviceGroupPresentCapabilities, Dispatch const & d ) const VULKAN_HPP_NOEXCEPT +@@ -15136,6 +15165,82 @@ namespace VULKAN_HPP_NAMESPACE + # endif /* VULKAN_HPP_DISABLE_ENHANCED_MODE */ + #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ //=== VK_OHOS_external_memory === ++ ++ template ++ VULKAN_HPP_NODISCARD VULKAN_HPP_INLINE Result ++ Device::getNativeBufferPropertiesOHOS( const struct OH_NativeBuffer * buffer, ++ VULKAN_HPP_NAMESPACE::NativeBufferPropertiesOHOS * pProperties, ++ Dispatch const & d ) const VULKAN_HPP_NOEXCEPT ++ { ++ VULKAN_HPP_ASSERT( d.getVkHeaderVersion() == VK_HEADER_VERSION ); ++ return static_cast( ++ d.vkGetNativeBufferPropertiesOHOS( m_device, buffer, reinterpret_cast( pProperties ) ) ); ++ } ++ ++# ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ++ template ++ VULKAN_HPP_NODISCARD VULKAN_HPP_INLINE typename ResultValueType::type ++ Device::getNativeBufferPropertiesOHOS( const struct OH_NativeBuffer & buffer, Dispatch const & d ) const ++ { ++ VULKAN_HPP_ASSERT( d.getVkHeaderVersion() == VK_HEADER_VERSION ); ++# if ( VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1 ) ++ VULKAN_HPP_ASSERT( d.vkGetNativeBufferPropertiesOHOS && ++ "Function requires " ); ++# endif ++ ++ VULKAN_HPP_NAMESPACE::NativeBufferPropertiesOHOS properties; ++ VULKAN_HPP_NAMESPACE::Result result = static_cast( ++ d.vkGetNativeBufferPropertiesOHOS( m_device, &buffer, reinterpret_cast( &properties ) ) ); ++ resultCheck( result, VULKAN_HPP_NAMESPACE_STRING "::Device::getNativeBufferPropertiesOHOS" ); ++ ++ return createResultValueType( result, properties ); ++ } ++ ++ template ++ VULKAN_HPP_NODISCARD VULKAN_HPP_INLINE typename ResultValueType>::type ++ Device::getNativeBufferPropertiesOHOS( const struct OH_NativeBuffer & buffer, Dispatch const & d ) const ++ { ++ VULKAN_HPP_ASSERT( d.getVkHeaderVersion() == VK_HEADER_VERSION ); ++# if ( VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1 ) ++ VULKAN_HPP_ASSERT( d.vkGetNativeBufferPropertiesOHOS && ++ "Function requires " ); ++# endif ++ ++ StructureChain structureChain; ++ VULKAN_HPP_NAMESPACE::NativeBufferPropertiesOHOS & properties = ++ structureChain.template get(); ++ VULKAN_HPP_NAMESPACE::Result result = static_cast( ++ d.vkGetNativeBufferPropertiesOHOS( m_device, &buffer, reinterpret_cast( &properties ) ) ); ++ resultCheck( result, VULKAN_HPP_NAMESPACE_STRING "::Device::getNativeBufferPropertiesOHOS" ); ++ ++ return createResultValueType( result, structureChain ); ++ } ++# endif /* VULKAN_HPP_DISABLE_ENHANCED_MODE */ ++ ++# ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ++ template ++ VULKAN_HPP_NODISCARD VULKAN_HPP_INLINE typename ResultValueType::type ++ Device::getMemoryNativeBufferOHOS( const VULKAN_HPP_NAMESPACE::MemoryGetNativeBufferInfoOHOS & info, Dispatch const & d ) const ++ { ++ VULKAN_HPP_ASSERT( d.getVkHeaderVersion() == VK_HEADER_VERSION ); ++# if ( VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1 ) ++ VULKAN_HPP_ASSERT( d.vkGetMemoryNativeBufferOHOS && ++ "Function requires " ); ++# endif ++ ++ struct OH_NativeBuffer * buffer; ++ VULKAN_HPP_NAMESPACE::Result result = static_cast( ++ d.vkGetMemoryNativeBufferOHOS( m_device, reinterpret_cast( &info ), &buffer ) ); ++ resultCheck( result, VULKAN_HPP_NAMESPACE_STRING "::Device::getMemoryNativeBufferOHOS" ); ++ ++ return createResultValueType( result, buffer ); ++ } ++# endif /* VULKAN_HPP_DISABLE_ENHANCED_MODE */ ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ ++ + #if defined( VK_ENABLE_BETA_EXTENSIONS ) + //=== VK_AMDX_shader_enqueue === + +diff --git a/include/vulkan/vulkan_handles.hpp b/include/vulkan/vulkan_handles.hpp +index e6b1481..c5e5e31 100644 +--- a/include/vulkan/vulkan_handles.hpp ++++ b/include/vulkan/vulkan_handles.hpp +@@ -829,6 +829,13 @@ namespace VULKAN_HPP_NAMESPACE + struct AndroidHardwareBufferFormatProperties2ANDROID; + #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ struct ExternalFormatOHOS; ++ struct NativeBufferPropertiesOHOS; ++ struct MemoryGetNativeBufferInfoOHOS; ++ struct NativeBufferFormatPropertiesOHOS; ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + #if defined( VK_ENABLE_BETA_EXTENSIONS ) + //=== VK_AMDX_shader_enqueue === + struct PhysicalDeviceShaderEnqueueFeaturesAMDX; +@@ -8701,6 +8708,16 @@ namespace VULKAN_HPP_NAMESPACE + Dispatch const & d VULKAN_HPP_DEFAULT_DISPATCHER_ASSIGNMENT ) const; + #endif /* VULKAN_HPP_DISABLE_ENHANCED_MODE */ + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++template ++ VULKAN_HPP_NODISCARD VULKAN_HPP_NAMESPACE::Result queueSignalReleaseImageOHOS(int32_t waitSemaphoreCount, ++ VULKAN_HPP_NAMESPACE::Semaphore *pWaitSemaphores, ++ VULKAN_HPP_NAMESPACE::Image pImage, ++ int32_t* pNativeFenceFd, ++ Dispatch const & d VULKAN_HPP_DEFAULT_DISPATCHER_ASSIGNMENT) const; ++ ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + //=== VK_EXT_debug_utils === + + template +@@ -11554,6 +11571,36 @@ namespace VULKAN_HPP_NAMESPACE + # endif /* VULKAN_HPP_DISABLE_ENHANCED_MODE */ + #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++ ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ //=== VK_OHOS_external_memory === ++ ++ template ++ VULKAN_HPP_NODISCARD Result ++ getNativeBufferPropertiesOHOS( const struct OH_NativeBuffer * buffer, ++ VULKAN_HPP_NAMESPACE::NativeBufferPropertiesOHOS * pProperties, ++ Dispatch const & d VULKAN_HPP_DEFAULT_DISPATCHER_ASSIGNMENT ) const VULKAN_HPP_NOEXCEPT; ++# ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ++ template ++ VULKAN_HPP_NODISCARD typename ResultValueType::type ++ getNativeBufferPropertiesOHOS( const struct OH_NativeBuffer & buffer, Dispatch const & d VULKAN_HPP_DEFAULT_DISPATCHER_ASSIGNMENT ) const; ++ template ++ VULKAN_HPP_NODISCARD typename ResultValueType>::type ++ getNativeBufferPropertiesOHOS( const struct OH_NativeBuffer & buffer, Dispatch const & d VULKAN_HPP_DEFAULT_DISPATCHER_ASSIGNMENT ) const; ++# endif /* VULKAN_HPP_DISABLE_ENHANCED_MODE */ ++ ++ template ++ VULKAN_HPP_NODISCARD Result getMemoryNativeBufferOHOS( const VULKAN_HPP_NAMESPACE::MemoryGetNativeBufferInfoOHOS * pInfo, ++ struct OH_NativeBuffer ** pBuffer, ++ Dispatch const & d VULKAN_HPP_DEFAULT_DISPATCHER_ASSIGNMENT ) const VULKAN_HPP_NOEXCEPT; ++# ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ++ template ++ VULKAN_HPP_NODISCARD typename ResultValueType::type ++ getMemoryNativeBufferOHOS( const VULKAN_HPP_NAMESPACE::MemoryGetNativeBufferInfoOHOS & info, ++ Dispatch const & d VULKAN_HPP_DEFAULT_DISPATCHER_ASSIGNMENT ) const; ++# endif /* VULKAN_HPP_DISABLE_ENHANCED_MODE */ ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + #if defined( VK_ENABLE_BETA_EXTENSIONS ) + //=== VK_AMDX_shader_enqueue === + +diff --git a/include/vulkan/vulkan_hash.hpp b/include/vulkan/vulkan_hash.hpp +index bebd7cb..199d073 100644 +--- a/include/vulkan/vulkan_hash.hpp ++++ b/include/vulkan/vulkan_hash.hpp +@@ -997,6 +997,30 @@ namespace std + }; + # endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++ ++# if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ template <> ++ struct hash ++ { ++ std::size_t operator()( VULKAN_HPP_NAMESPACE::NativeBufferFormatPropertiesOHOS const & nativeBufferFormatPropertiesOHOS ) const ++ VULKAN_HPP_NOEXCEPT ++ { ++ std::size_t seed = 0; ++VULKAN_HPP_HASH_COMBINE( seed, nativeBufferFormatPropertiesOHOS.sType ); ++ VULKAN_HPP_HASH_COMBINE( seed, nativeBufferFormatPropertiesOHOS.pNext ); ++ VULKAN_HPP_HASH_COMBINE( seed, nativeBufferFormatPropertiesOHOS.format ); ++ VULKAN_HPP_HASH_COMBINE( seed, nativeBufferFormatPropertiesOHOS.externalFormat ); ++ VULKAN_HPP_HASH_COMBINE( seed, nativeBufferFormatPropertiesOHOS.formatFeatures ); ++ VULKAN_HPP_HASH_COMBINE( seed, nativeBufferFormatPropertiesOHOS.samplerYcbcrConversionComponents ); ++ VULKAN_HPP_HASH_COMBINE( seed, nativeBufferFormatPropertiesOHOS.suggestedYcbcrModel ); ++ VULKAN_HPP_HASH_COMBINE( seed, nativeBufferFormatPropertiesOHOS.suggestedYcbcrRange ); ++ VULKAN_HPP_HASH_COMBINE( seed, nativeBufferFormatPropertiesOHOS.suggestedXChromaOffset ); ++ VULKAN_HPP_HASH_COMBINE( seed, nativeBufferFormatPropertiesOHOS.suggestedYChromaOffset ); ++ return seed; ++ } ++ }; ++# endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + # if defined( VK_USE_PLATFORM_ANDROID_KHR ) + template <> + struct hash +@@ -4825,6 +4849,22 @@ namespace std + }; + # endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++# if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ template <> ++ struct hash ++ { ++ std::size_t operator()( VULKAN_HPP_NAMESPACE::ExternalFormatOHOS const & externalFormatOHOS ) const VULKAN_HPP_NOEXCEPT ++ { ++ std::size_t seed = 0; ++ VULKAN_HPP_HASH_COMBINE( seed, externalFormatOHOS.sType ); ++ VULKAN_HPP_HASH_COMBINE( seed, externalFormatOHOS.pNext ); ++ VULKAN_HPP_HASH_COMBINE( seed, externalFormatOHOS.externalFormat ); ++ return seed; ++ } ++ }; ++# endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ ++ + # if defined( VK_USE_PLATFORM_SCREEN_QNX ) + template <> + struct hash +@@ -6714,6 +6754,22 @@ namespace std + }; + # endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++# if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ template <> ++ struct hash ++ { ++ std::size_t ++ operator()( VULKAN_HPP_NAMESPACE::MemoryGetNativeBufferInfoOHOS const & memoryGetNativeBufferInfoOHOS ) const VULKAN_HPP_NOEXCEPT ++ { ++ std::size_t seed = 0; ++ VULKAN_HPP_HASH_COMBINE( seed, memoryGetNativeBufferInfoOHOS.sType ); ++ VULKAN_HPP_HASH_COMBINE( seed, memoryGetNativeBufferInfoOHOS.pNext ); ++ VULKAN_HPP_HASH_COMBINE( seed, memoryGetNativeBufferInfoOHOS.memory ); ++ return seed; ++ } ++ }; ++# endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + template <> + struct hash + { +diff --git a/include/vulkan/vulkan_ohos.h b/include/vulkan/vulkan_ohos.h +new file mode 100644 +index 0000000..e0bf8c6 +--- /dev/null ++++ b/include/vulkan/vulkan_ohos.h +@@ -0,0 +1,199 @@ ++#ifndef VULKAN_OHOS_H_ ++#define VULKAN_OHOS_H_ 1 ++ ++/* ++** Copyright 2015-2022 The Khronos Group Inc. ++** ++** SPDX-License-Identifier: Apache-2.0 ++*/ ++ ++/* ++** This header is generated from the Khronos Vulkan XML API Registry. ++** ++*/ ++ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++ ++#define VK_OHOS_surface 1 ++typedef struct NativeWindow OHNativeWindow; ++#define VK_OHOS_SURFACE_SPEC_VERSION 1 ++#define VK_OHOS_SURFACE_EXTENSION_NAME "VK_OHOS_surface" ++typedef VkFlags VkSurfaceCreateFlagsOHOS; ++typedef struct VkSurfaceCreateInfoOHOS { ++ VkStructureType sType; ++ const void* pNext; ++ VkSurfaceCreateFlagsOHOS flags; ++ OHNativeWindow* window; ++} VkSurfaceCreateInfoOHOS; ++ ++typedef VkResult (VKAPI_PTR *PFN_vkCreateSurfaceOHOS)(VkInstance instance, const VkSurfaceCreateInfoOHOS* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); ++ ++#ifndef VK_NO_PROTOTYPES ++VKAPI_ATTR VkResult VKAPI_CALL vkCreateSurfaceOHOS( ++ VkInstance instance, ++ const VkSurfaceCreateInfoOHOS* pCreateInfo, ++ const VkAllocationCallbacks* pAllocator, ++ VkSurfaceKHR* pSurface); ++#endif ++ ++ ++#define VK_OHOS_native_buffer 1 ++struct OHBufferHandle; ++#define VK_OHOS_NATIVE_BUFFER_SPEC_VERSION 1 ++#define VK_OHOS_NATIVE_BUFFER_EXTENSION_NAME "VK_OHOS_native_buffer" ++ ++typedef enum VkSwapchainImageUsageFlagBitsOHOS { ++ VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_OHOS = 0x00000001, ++ VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM_OHOS = 0x7FFFFFFF ++} VkSwapchainImageUsageFlagBitsOHOS; ++typedef VkFlags VkSwapchainImageUsageFlagsOHOS; ++typedef struct VkNativeBufferOHOS { ++ VkStructureType sType; ++ const void* pNext; ++ struct OHBufferHandle* handle; ++} VkNativeBufferOHOS; ++ ++typedef struct VkSwapchainImageCreateInfoOHOS { ++ VkStructureType sType; ++ const void* pNext; ++ VkSwapchainImageUsageFlagsOHOS usage; ++} VkSwapchainImageCreateInfoOHOS; ++ ++typedef struct VkPhysicalDevicePresentationPropertiesOHOS { ++ VkStructureType sType; ++ const void* pNext; ++ VkBool32 sharedImage; ++} VkPhysicalDevicePresentationPropertiesOHOS; ++ ++/** ++ * @brief this type is deprecated, please use PFN_vkAcquireImageOHOS instead ++ * @deprecated ++ */ ++typedef VkResult (VKAPI_PTR *PFN_vkSetNativeFenceFdOpenHarmony)(VkDevice device, int32_t nativeFenceFd, VkSemaphore semaphore, VkFence fence); ++ ++/** ++ * @brief this type is deprecated, please use PFN_vkQueueSignalReleaseImageOHOS instead ++ * @deprecated ++ */ ++typedef VkResult (VKAPI_PTR *PFN_vkGetNativeFenceFdOpenHarmony)(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int32_t* pNativeFenceFd); ++typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageOHOS)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, uint64_t* grallocUsage); ++typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageOHOS)(VkDevice device, VkImage image, int32_t nativeFenceFd, VkSemaphore semaphore, VkFence fence); ++typedef VkResult (VKAPI_PTR *PFN_vkQueueSignalReleaseImageOHOS)(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int32_t* pNativeFenceFd); ++ ++#ifndef VK_NO_PROTOTYPES ++/** ++ * @brief this interface is deprecated, please use vkAcquireImageOHOS instead ++ * @deprecated ++ */ ++VKAPI_ATTR VkResult VKAPI_CALL vkSetNativeFenceFdOpenHarmony( ++ VkDevice device, ++ int32_t nativeFenceFd, ++ VkSemaphore semaphore, ++ VkFence fence); ++ ++/** ++ * @brief this interface is deprecated, please use vkQueueSignalReleaseImageOHOS instead ++ * @deprecated ++ */ ++VKAPI_ATTR VkResult VKAPI_CALL vkGetNativeFenceFdOpenHarmony( ++ VkQueue queue, ++ uint32_t waitSemaphoreCount, ++ const VkSemaphore* pWaitSemaphores, ++ VkImage image, ++ int32_t* pNativeFenceFd); ++ ++VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsageOHOS( ++ VkDevice device, ++ VkFormat format, ++ VkImageUsageFlags imageUsage, ++ uint64_t* grallocUsage); ++ ++VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageOHOS( ++ VkDevice device, ++ VkImage image, ++ int32_t nativeFenceFd, ++ VkSemaphore semaphore, ++ VkFence fence); ++ ++VKAPI_ATTR VkResult VKAPI_CALL vkQueueSignalReleaseImageOHOS( ++ VkQueue queue, ++ uint32_t waitSemaphoreCount, ++ const VkSemaphore* pWaitSemaphores, ++ VkImage image, ++ int32_t* pNativeFenceFd); ++#endif ++ ++ ++#define VK_OHOS_external_memory 1 ++struct OH_NativeBuffer; ++#define VK_OHOS_EXTERNAL_MEMORY_SPEC_VERSION 1 ++#define VK_OHOS_EXTERNAL_MEMORY_EXTENSION_NAME "VK_OHOS_external_memory" ++typedef struct VkNativeBufferUsageOHOS { ++ VkStructureType sType; ++ void* pNext; ++ uint64_t OHOSNativeBufferUsage; ++} VkNativeBufferUsageOHOS; ++ ++typedef struct VkNativeBufferPropertiesOHOS { ++ VkStructureType sType; ++ void* pNext; ++ VkDeviceSize allocationSize; ++ uint32_t memoryTypeBits; ++} VkNativeBufferPropertiesOHOS; ++ ++typedef struct VkNativeBufferFormatPropertiesOHOS { ++ VkStructureType sType; ++ void* pNext; ++ VkFormat format; ++ uint64_t externalFormat; ++ VkFormatFeatureFlags formatFeatures; ++ VkComponentMapping samplerYcbcrConversionComponents; ++ VkSamplerYcbcrModelConversion suggestedYcbcrModel; ++ VkSamplerYcbcrRange suggestedYcbcrRange; ++ VkChromaLocation suggestedXChromaOffset; ++ VkChromaLocation suggestedYChromaOffset; ++} VkNativeBufferFormatPropertiesOHOS; ++ ++typedef struct VkImportNativeBufferInfoOHOS { ++ VkStructureType sType; ++ const void* pNext; ++ struct OH_NativeBuffer* buffer; ++} VkImportNativeBufferInfoOHOS; ++ ++typedef struct VkMemoryGetNativeBufferInfoOHOS { ++ VkStructureType sType; ++ const void* pNext; ++ VkDeviceMemory memory; ++} VkMemoryGetNativeBufferInfoOHOS; ++ ++typedef struct VkExternalFormatOHOS { ++ VkStructureType sType; ++ void* pNext; ++ uint64_t externalFormat; ++} VkExternalFormatOHOS; ++ ++typedef VkResult (VKAPI_PTR *PFN_vkGetNativeBufferPropertiesOHOS)(VkDevice device, const struct OH_NativeBuffer* buffer, VkNativeBufferPropertiesOHOS* pProperties); ++typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryNativeBufferOHOS)(VkDevice device, const VkMemoryGetNativeBufferInfoOHOS* pInfo, struct OH_NativeBuffer** pBuffer); ++ ++#ifndef VK_NO_PROTOTYPES ++VKAPI_ATTR VkResult VKAPI_CALL vkGetNativeBufferPropertiesOHOS( ++ VkDevice device, ++ const struct OH_NativeBuffer* buffer, ++ VkNativeBufferPropertiesOHOS* pProperties); ++ ++VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryNativeBufferOHOS( ++ VkDevice device, ++ const VkMemoryGetNativeBufferInfoOHOS* pInfo, ++ struct OH_NativeBuffer** pBuffer); ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/include/vulkan/vulkan_raii.hpp b/include/vulkan/vulkan_raii.hpp +index e97bfb3..e918f14 100644 +--- a/include/vulkan/vulkan_raii.hpp ++++ b/include/vulkan/vulkan_raii.hpp +@@ -879,6 +879,10 @@ namespace VULKAN_HPP_NAMESPACE + PFN_vkGetDeviceGroupSurfacePresentModesKHR( vkGetDeviceProcAddr( device, "vkGetDeviceGroupSurfacePresentModesKHR" ) ); + vkAcquireNextImage2KHR = PFN_vkAcquireNextImage2KHR( vkGetDeviceProcAddr( device, "vkAcquireNextImage2KHR" ) ); + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ vkQueueSignalReleaseImageOHOS = PFN_vkQueueSignalReleaseImageOHOS( vkGetDeviceProcAddr( device, "vkQueueSignalReleaseImageOHOS" ) ); ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + //=== VK_KHR_display_swapchain === + vkCreateSharedSwapchainsKHR = PFN_vkCreateSharedSwapchainsKHR( vkGetDeviceProcAddr( device, "vkCreateSharedSwapchainsKHR" ) ); + +@@ -1078,6 +1082,14 @@ namespace VULKAN_HPP_NAMESPACE + PFN_vkGetMemoryAndroidHardwareBufferANDROID( vkGetDeviceProcAddr( device, "vkGetMemoryAndroidHardwareBufferANDROID" ) ); + # endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ //=== VK_OHOS_external_memory === ++ vkGetNativeBufferPropertiesOHOS = ++ PFN_vkGetNativeBufferPropertiesOHOS( vkGetDeviceProcAddr( device, "vkGetNativeBufferPropertiesOHOS" ) ); ++ vkGetMemoryNativeBufferOHOS = ++ PFN_vkGetMemoryNativeBufferOHOS( vkGetDeviceProcAddr( device, "vkGetMemoryNativeBufferOHOS" ) ); ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + # if defined( VK_ENABLE_BETA_EXTENSIONS ) + //=== VK_AMDX_shader_enqueue === + vkCreateExecutionGraphPipelinesAMDX = PFN_vkCreateExecutionGraphPipelinesAMDX( vkGetDeviceProcAddr( device, "vkCreateExecutionGraphPipelinesAMDX" ) ); +@@ -1929,6 +1941,10 @@ namespace VULKAN_HPP_NAMESPACE + PFN_vkGetDeviceGroupSurfacePresentModesKHR vkGetDeviceGroupSurfacePresentModesKHR = 0; + PFN_vkAcquireNextImage2KHR vkAcquireNextImage2KHR = 0; + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ PFN_vkQueueSignalReleaseImageOHOS vkQueueSignalReleaseImageOHOS = 0; ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + //=== VK_KHR_display_swapchain === + PFN_vkCreateSharedSwapchainsKHR vkCreateSharedSwapchainsKHR = 0; + +@@ -2104,6 +2120,15 @@ namespace VULKAN_HPP_NAMESPACE + PFN_dummy vkGetMemoryAndroidHardwareBufferANDROID_placeholder = 0; + # endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ //=== VK_OHOS_external_memory === ++ PFN_vkGetNativeBufferPropertiesOHOS vkGetNativeBufferPropertiesOHOS = 0; ++ PFN_vkGetMemoryNativeBufferOHOS vkGetMemoryNativeBufferOHOS = 0; ++#else ++ PFN_dummy vkGetNativeBufferPropertiesOHOS_placeholder = 0; ++ PFN_dummy vkGetMemoryNativeBufferOHOS_placeholder = 0; ++#endif /*VK_USE_PLATFORM_ANDROID_KHR*/ ++ + # if defined( VK_ENABLE_BETA_EXTENSIONS ) + //=== VK_AMDX_shader_enqueue === + PFN_vkCreateExecutionGraphPipelinesAMDX vkCreateExecutionGraphPipelinesAMDX = 0; +@@ -4057,6 +4082,20 @@ namespace VULKAN_HPP_NAMESPACE + getMemoryAndroidHardwareBufferANDROID( const VULKAN_HPP_NAMESPACE::MemoryGetAndroidHardwareBufferInfoANDROID & info ) const; + # endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++# if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ //=== VK_OHOS_external_memory === ++ ++ VULKAN_HPP_NODISCARD VULKAN_HPP_NAMESPACE::NativeBufferPropertiesOHOS ++ getNativeBufferPropertiesOHOS( const struct OH_NativeBuffer & buffer ) const; ++ ++ template ++ VULKAN_HPP_NODISCARD VULKAN_HPP_NAMESPACE::StructureChain ++ getNativeBufferPropertiesOHOS( const struct OH_NativeBuffer & buffer ) const; ++ ++ VULKAN_HPP_NODISCARD struct OH_NativeBuffer * ++ getMemoryNativeBufferOHOS( const VULKAN_HPP_NAMESPACE::MemoryGetNativeBufferInfoOHOS & info ) const; ++# endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + # if defined( VK_ENABLE_BETA_EXTENSIONS ) + //=== VK_AMDX_shader_enqueue === + +@@ -10252,6 +10291,11 @@ namespace VULKAN_HPP_NAMESPACE + + VULKAN_HPP_NODISCARD VULKAN_HPP_NAMESPACE::Result presentKHR( const VULKAN_HPP_NAMESPACE::PresentInfoKHR & presentInfo ) const; + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ VULKAN_HPP_NODISCARD VULKAN_HPP_INLINE VULKAN_HPP_NAMESPACE::Result queueSignalReleaseImageOHOS(int32_t waitSemaphoreCount, ++ VULKAN_HPP_NAMESPACE::Semaphore *pWaitSemaphores, VULKAN_HPP_NAMESPACE::Image pImage, int32_t* pNativeFenceFd) const; ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + //=== VK_EXT_debug_utils === + + void beginDebugUtilsLabelEXT( const VULKAN_HPP_NAMESPACE::DebugUtilsLabelEXT & labelInfo ) const VULKAN_HPP_NOEXCEPT; +@@ -15142,6 +15186,30 @@ namespace VULKAN_HPP_NAMESPACE + return static_cast( result ); + } + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ VULKAN_HPP_NODISCARD VULKAN_HPP_INLINE VULKAN_HPP_NAMESPACE::Result Queue::queueSignalReleaseImageOHOS(int32_t waitSemaphoreCount, ++ VULKAN_HPP_NAMESPACE::Semaphore *pWaitSemaphores, VULKAN_HPP_NAMESPACE::Image pImage, int32_t* pNativeFenceFd) const ++ { ++ VULKAN_HPP_ASSERT( getDispatcher()->vkQueueSignalReleaseImageOHOS && "Function requires " ); ++ ++ VkSemaphore *semaphores; ++ semaphores = new VkSemaphore[waitSemaphoreCount]; ++ for (int i = 0; i < waitSemaphoreCount; i++) { ++ semaphores[i] = static_cast(pWaitSemaphores[i]); ++ } ++ ++ VULKAN_HPP_NAMESPACE::Result result = static_cast( ++ getDispatcher()->vkQueueSignalReleaseImageOHOS( static_cast( m_queue ), waitSemaphoreCount, ++ semaphores, ++ static_cast( pImage ), ++ pNativeFenceFd)); ++ ++ free(semaphores); ++ ++ return static_cast( result ); ++ } ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + VULKAN_HPP_NODISCARD VULKAN_HPP_INLINE VULKAN_HPP_NAMESPACE::DeviceGroupPresentCapabilitiesKHR Device::getGroupPresentCapabilitiesKHR() const + { + VULKAN_HPP_ASSERT( getDispatcher()->vkGetDeviceGroupPresentCapabilitiesKHR && +@@ -17773,6 +17841,53 @@ namespace VULKAN_HPP_NAMESPACE + } + # endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++# if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ VULKAN_HPP_NODISCARD VULKAN_HPP_INLINE VULKAN_HPP_NAMESPACE::NativeBufferPropertiesOHOS ++ Device::getNativeBufferPropertiesOHOS( const struct OH_NativeBuffer* buffer ) const ++ { ++ VULKAN_HPP_ASSERT( getDispatcher()->VkNativeBufferPropertiesOHOS && ++ "Function requires " ); ++ ++ VULKAN_HPP_NAMESPACE::NativeBufferPropertiesOHOS properties; ++ VULKAN_HPP_NAMESPACE::Result result = static_cast( getDispatcher()->VkNativeBufferPropertiesOHOS( ++ static_cast( m_device ), &buffer, reinterpret_cast( &properties ) ) ); ++ resultCheck( result, VULKAN_HPP_NAMESPACE_STRING "::Device::getAndroidHardwareBufferPropertiesANDROID" ); ++ ++ return properties; ++ } ++ ++ template ++ VULKAN_HPP_NODISCARD VULKAN_HPP_INLINE VULKAN_HPP_NAMESPACE::StructureChain ++ Device::getNativeBufferPropertiesOHOS( const struct OH_NativeBuffer* buffer ) const ++ { ++ VULKAN_HPP_ASSERT( getDispatcher()->vkGetNativeBufferPropertiesOHOS && ++ "Function requires " ); ++ ++ StructureChain structureChain; ++ VULKAN_HPP_NAMESPACE::NativeBufferPropertiesOHOS & properties = ++ structureChain.template get(); ++ VULKAN_HPP_NAMESPACE::Result result = static_cast( getDispatcher()->VkNativeBufferPropertiesOHOS( ++ static_cast( m_device ), &buffer, reinterpret_cast( &properties ) ) ); ++ resultCheck( result, VULKAN_HPP_NAMESPACE_STRING "::Device::getAndroidHardwareBufferPropertiesANDROID" ); ++ ++ return structureChain; ++ } ++ ++ VULKAN_HPP_NODISCARD VULKAN_HPP_INLINE struct OH_NativeBuffer * ++ Device::getMemoryNativeBufferOHOS( const VULKAN_HPP_NAMESPACE::MemoryGetNativeBufferInfoOHOS & info ) const ++ { ++ VULKAN_HPP_ASSERT( getDispatcher()->vkGetMemoryNativeBufferOHOS && ++ "Function requires " ); ++ ++ struct OH_NativeBuffer * buffer; ++ VULKAN_HPP_NAMESPACE::Result result = static_cast( getDispatcher()->vkGetMemoryNativeBufferOHOS( ++ static_cast( m_device ), reinterpret_cast( &info ), &buffer ) ); ++ resultCheck( result, VULKAN_HPP_NAMESPACE_STRING "::Device::getMemoryNativeBufferOHOS" ); ++ ++ return buffer; ++ } ++# endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + # if defined( VK_ENABLE_BETA_EXTENSIONS ) + //=== VK_AMDX_shader_enqueue === + +diff --git a/include/vulkan/vulkan_static_assertions.hpp b/include/vulkan/vulkan_static_assertions.hpp +index 30f287a..285b872 100644 +--- a/include/vulkan/vulkan_static_assertions.hpp ++++ b/include/vulkan/vulkan_static_assertions.hpp +@@ -3226,6 +3226,34 @@ VULKAN_HPP_STATIC_ASSERT( std::is_nothrow_move_constructible::value, "struct wrapper is not a standard layout!" ); ++VULKAN_HPP_STATIC_ASSERT( std::is_nothrow_move_constructible::value, ++ "ExternalFormatOHOS is not nothrow_move_constructible!" ); ++VULKAN_HPP_STATIC_ASSERT( sizeof( VULKAN_HPP_NAMESPACE::NativeBufferPropertiesOHOS ) == sizeof( VkNativeBufferPropertiesOHOS ), ++ "struct and wrapper have different size!" ); ++VULKAN_HPP_STATIC_ASSERT( std::is_standard_layout::value, ++ "struct wrapper is not a standard layout!" ); ++VULKAN_HPP_STATIC_ASSERT( std::is_nothrow_move_constructible::value, ++ "NativeBufferPropertiesOHOS is not nothrow_move_constructible!" ); ++VULKAN_HPP_STATIC_ASSERT( sizeof( VULKAN_HPP_NAMESPACE::MemoryGetNativeBufferInfoOHOS ) == sizeof( VkMemoryGetNativeBufferInfoOHOS ), ++ "struct and wrapper have different size!" ); ++VULKAN_HPP_STATIC_ASSERT( std::is_standard_layout::value, ++ "struct wrapper is not a standard layout!" ); ++VULKAN_HPP_STATIC_ASSERT( std::is_nothrow_move_constructible::value, ++ "MemoryGetNativeBufferInfoOHOS is not nothrow_move_constructible!" ); ++VULKAN_HPP_STATIC_ASSERT( sizeof( VULKAN_HPP_NAMESPACE::NativeBufferFormatPropertiesOHOS ) == ++ sizeof( VkNativeBufferFormatPropertiesOHOS ), ++ "struct and wrapper have different size!" ); ++VULKAN_HPP_STATIC_ASSERT( std::is_standard_layout::value, ++ "struct wrapper is not a standard layout!" ); ++VULKAN_HPP_STATIC_ASSERT( std::is_nothrow_move_constructible::value, ++ "NativeBufferFormatPropertiesOHOS is not nothrow_move_constructible!" ); ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + #if defined( VK_ENABLE_BETA_EXTENSIONS ) + //=== VK_AMDX_shader_enqueue === + +diff --git a/include/vulkan/vulkan_structs.hpp b/include/vulkan/vulkan_structs.hpp +index c18fbe9..4666d6f 100644 +--- a/include/vulkan/vulkan_structs.hpp ++++ b/include/vulkan/vulkan_structs.hpp +@@ -5072,6 +5072,134 @@ namespace VULKAN_HPP_NAMESPACE + }; + #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ struct NativeBufferFormatPropertiesOHOS ++ { ++ using NativeType = VkNativeBufferFormatPropertiesOHOS; ++ ++ static const bool allowDuplicate = false; ++ static VULKAN_HPP_CONST_OR_CONSTEXPR StructureType structureType = StructureType::eNativeBufferFormatPropertiesOHOS; ++ ++# if !defined( VULKAN_HPP_NO_STRUCT_CONSTRUCTORS ) ++ VULKAN_HPP_CONSTEXPR NativeBufferFormatPropertiesOHOS( ++ VULKAN_HPP_NAMESPACE::Format format_ = VULKAN_HPP_NAMESPACE::Format::eUndefined, ++ uint64_t externalFormat_ = {}, ++ VULKAN_HPP_NAMESPACE::FormatFeatureFlags formatFeatures_ = {}, ++ VULKAN_HPP_NAMESPACE::ComponentMapping samplerYcbcrConversionComponents_ = {}, ++ VULKAN_HPP_NAMESPACE::SamplerYcbcrModelConversion suggestedYcbcrModel_ = VULKAN_HPP_NAMESPACE::SamplerYcbcrModelConversion::eRgbIdentity, ++ VULKAN_HPP_NAMESPACE::SamplerYcbcrRange suggestedYcbcrRange_ = VULKAN_HPP_NAMESPACE::SamplerYcbcrRange::eItuFull, ++ VULKAN_HPP_NAMESPACE::ChromaLocation suggestedXChromaOffset_ = VULKAN_HPP_NAMESPACE::ChromaLocation::eCositedEven, ++ VULKAN_HPP_NAMESPACE::ChromaLocation suggestedYChromaOffset_ = VULKAN_HPP_NAMESPACE::ChromaLocation::eCositedEven, ++ void * pNext_ = nullptr ) VULKAN_HPP_NOEXCEPT ++ : pNext( pNext_ ) ++ , format( format_ ) ++ , externalFormat( externalFormat_ ) ++ , formatFeatures( formatFeatures_ ) ++ , samplerYcbcrConversionComponents( samplerYcbcrConversionComponents_ ) ++ , suggestedYcbcrModel( suggestedYcbcrModel_ ) ++ , suggestedYcbcrRange( suggestedYcbcrRange_ ) ++ , suggestedXChromaOffset( suggestedXChromaOffset_ ) ++ , suggestedYChromaOffset( suggestedYChromaOffset_ ) ++ { ++ } ++ ++ VULKAN_HPP_CONSTEXPR NativeBufferFormatPropertiesOHOS( NativeBufferFormatPropertiesOHOS const & rhs ) VULKAN_HPP_NOEXCEPT = default; ++ ++ NativeBufferFormatPropertiesOHOS( VkNativeBufferFormatPropertiesOHOS const & rhs ) VULKAN_HPP_NOEXCEPT ++ : NativeBufferFormatPropertiesOHOS( *reinterpret_cast( &rhs ) ) ++ { ++ } ++ ++ NativeBufferFormatPropertiesOHOS & operator=( NativeBufferFormatPropertiesOHOS const & rhs ) VULKAN_HPP_NOEXCEPT = default; ++# endif /*VULKAN_HPP_NO_STRUCT_CONSTRUCTORS*/ ++ ++ NativeBufferFormatPropertiesOHOS & operator=( VkNativeBufferFormatPropertiesOHOS const & rhs ) VULKAN_HPP_NOEXCEPT ++ { ++ *this = *reinterpret_cast( &rhs ); ++ return *this; ++ } ++ ++ operator VkNativeBufferFormatPropertiesOHOS const &() const VULKAN_HPP_NOEXCEPT ++ { ++ return *reinterpret_cast( this ); ++ } ++ ++ operator VkNativeBufferFormatPropertiesOHOS &() VULKAN_HPP_NOEXCEPT ++ { ++ return *reinterpret_cast( this ); ++ } ++ ++# if defined( VULKAN_HPP_USE_REFLECT ) ++# if 14 <= VULKAN_HPP_CPP_VERSION ++ auto ++# else ++ std::tuple ++# endif ++ reflect() const VULKAN_HPP_NOEXCEPT ++ { ++ return std::tie( sType, ++ pNext, ++ format, ++ externalFormat, ++ formatFeatures, ++ samplerYcbcrConversionComponents, ++ suggestedYcbcrModel, ++ suggestedYcbcrRange, ++ suggestedXChromaOffset, ++ suggestedYChromaOffset ); ++ } ++# endif ++ ++# if defined( VULKAN_HPP_HAS_SPACESHIP_OPERATOR ) ++ auto operator<=>( NativeBufferFormatPropertiesOHOS const & ) const = default; ++# else ++ bool operator==( NativeBufferFormatPropertiesOHOS const & rhs ) const VULKAN_HPP_NOEXCEPT ++ { ++# if defined( VULKAN_HPP_USE_REFLECT ) ++ return this->reflect() == rhs.reflect(); ++# else ++ return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( format == rhs.format ) && ( externalFormat == rhs.externalFormat ) && ++ ( formatFeatures == rhs.formatFeatures ) && ( samplerYcbcrConversionComponents == rhs.samplerYcbcrConversionComponents ) && ++ ( suggestedYcbcrModel == rhs.suggestedYcbcrModel ) && ( suggestedYcbcrRange == rhs.suggestedYcbcrRange ) && ++ ( suggestedXChromaOffset == rhs.suggestedXChromaOffset ) && ( suggestedYChromaOffset == rhs.suggestedYChromaOffset ); ++# endif ++ } ++ ++ bool operator!=( NativeBufferFormatPropertiesOHOS const & rhs ) const VULKAN_HPP_NOEXCEPT ++ { ++ return !operator==( rhs ); ++ } ++# endif ++ ++ public: ++ VULKAN_HPP_NAMESPACE::StructureType sType = StructureType::eNativeBufferFormatPropertiesOHOS; ++ void * pNext = {}; ++ VULKAN_HPP_NAMESPACE::Format format = VULKAN_HPP_NAMESPACE::Format::eUndefined; ++ uint64_t externalFormat = {}; ++ VULKAN_HPP_NAMESPACE::FormatFeatureFlags formatFeatures = {}; ++ VULKAN_HPP_NAMESPACE::ComponentMapping samplerYcbcrConversionComponents = {}; ++ VULKAN_HPP_NAMESPACE::SamplerYcbcrModelConversion suggestedYcbcrModel = VULKAN_HPP_NAMESPACE::SamplerYcbcrModelConversion::eRgbIdentity; ++ VULKAN_HPP_NAMESPACE::SamplerYcbcrRange suggestedYcbcrRange = VULKAN_HPP_NAMESPACE::SamplerYcbcrRange::eItuFull; ++ VULKAN_HPP_NAMESPACE::ChromaLocation suggestedXChromaOffset = VULKAN_HPP_NAMESPACE::ChromaLocation::eCositedEven; ++ VULKAN_HPP_NAMESPACE::ChromaLocation suggestedYChromaOffset = VULKAN_HPP_NAMESPACE::ChromaLocation::eCositedEven; ++ }; ++ ++ template <> ++ struct CppType ++ { ++ using Type = NativeBufferFormatPropertiesOHOS; ++ }; ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + #if defined( VK_USE_PLATFORM_ANDROID_KHR ) + struct AndroidHardwareBufferFormatResolvePropertiesANDROID + { +@@ -5248,6 +5376,94 @@ namespace VULKAN_HPP_NAMESPACE + }; + #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ struct NativeBufferPropertiesOHOS ++ { ++ using NativeType = VkNativeBufferPropertiesOHOS; ++ ++ static const bool allowDuplicate = false; ++ static VULKAN_HPP_CONST_OR_CONSTEXPR StructureType structureType = StructureType::eNativeBufferPropertiesOHOS; ++ ++# if !defined( VULKAN_HPP_NO_STRUCT_CONSTRUCTORS ) ++ VULKAN_HPP_CONSTEXPR NativeBufferPropertiesOHOS( VULKAN_HPP_NAMESPACE::DeviceSize allocationSize_ = {}, ++ uint32_t memoryTypeBits_ = {}, ++ void * pNext_ = nullptr ) VULKAN_HPP_NOEXCEPT ++ : pNext( pNext_ ) ++ , allocationSize( allocationSize_ ) ++ , memoryTypeBits( memoryTypeBits_ ) ++ { ++ } ++ ++ VULKAN_HPP_CONSTEXPR NativeBufferPropertiesOHOS( NativeBufferPropertiesOHOS const & rhs ) VULKAN_HPP_NOEXCEPT = default; ++ ++ NativeBufferPropertiesOHOS( VkNativeBufferPropertiesOHOS const & rhs ) VULKAN_HPP_NOEXCEPT ++ : NativeBufferPropertiesOHOS( *reinterpret_cast( &rhs ) ) ++ { ++ } ++ ++ NativeBufferPropertiesOHOS & operator=( NativeBufferPropertiesOHOS const & rhs ) VULKAN_HPP_NOEXCEPT = default; ++# endif /*VULKAN_HPP_NO_STRUCT_CONSTRUCTORS*/ ++ ++ NativeBufferPropertiesOHOS & operator=( VkNativeBufferPropertiesOHOS const & rhs ) VULKAN_HPP_NOEXCEPT ++ { ++ *this = *reinterpret_cast( &rhs ); ++ return *this; ++ } ++ ++ operator VkNativeBufferPropertiesOHOS const &() const VULKAN_HPP_NOEXCEPT ++ { ++ return *reinterpret_cast( this ); ++ } ++ ++ operator VkNativeBufferPropertiesOHOS &() VULKAN_HPP_NOEXCEPT ++ { ++ return *reinterpret_cast( this ); ++ } ++ ++# if defined( VULKAN_HPP_USE_REFLECT ) ++# if 14 <= VULKAN_HPP_CPP_VERSION ++ auto ++# else ++ std::tuple ++# endif ++ reflect() const VULKAN_HPP_NOEXCEPT ++ { ++ return std::tie( sType, pNext, allocationSize, memoryTypeBits ); ++ } ++# endif ++ ++# if defined( VULKAN_HPP_HAS_SPACESHIP_OPERATOR ) ++ auto operator<=>( NativeBufferPropertiesOHOS const & ) const = default; ++# else ++ bool operator==( NativeBufferPropertiesOHOS const & rhs ) const VULKAN_HPP_NOEXCEPT ++ { ++# if defined( VULKAN_HPP_USE_REFLECT ) ++ return this->reflect() == rhs.reflect(); ++# else ++ return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( allocationSize == rhs.allocationSize ) && ( memoryTypeBits == rhs.memoryTypeBits ); ++# endif ++ } ++ ++ bool operator!=( NativeBufferPropertiesOHOS const & rhs ) const VULKAN_HPP_NOEXCEPT ++ { ++ return !operator==( rhs ); ++ } ++# endif ++ ++ public: ++ VULKAN_HPP_NAMESPACE::StructureType sType = StructureType::eNativeBufferPropertiesOHOS; ++ void * pNext = {}; ++ VULKAN_HPP_NAMESPACE::DeviceSize allocationSize = {}; ++ uint32_t memoryTypeBits = {}; ++ }; ++ ++ template <> ++ struct CppType ++ { ++ using Type = NativeBufferPropertiesOHOS; ++ }; ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + #if defined( VK_USE_PLATFORM_ANDROID_KHR ) + struct AndroidHardwareBufferUsageANDROID + { +@@ -35992,6 +36208,107 @@ namespace VULKAN_HPP_NAMESPACE + }; + #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++ ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ ++ struct ExternalFormatOHOS ++ { ++ using NativeType = ExternalFormatOHOS; ++ ++ static const bool allowDuplicate = false; ++ static VULKAN_HPP_CONST_OR_CONSTEXPR StructureType structureType = StructureType::eExternalFormatOHOS; ++ ++# if !defined( VULKAN_HPP_NO_STRUCT_CONSTRUCTORS ) ++ VULKAN_HPP_CONSTEXPR ExternalFormatOHOS( uint64_t externalFormat_ = {}, void * pNext_ = nullptr ) VULKAN_HPP_NOEXCEPT ++ : pNext( pNext_ ) ++ , externalFormat( externalFormat_ ) ++ { ++ } ++ ++ VULKAN_HPP_CONSTEXPR ExternalFormatOHOS( ExternalFormatOHOS const & rhs ) VULKAN_HPP_NOEXCEPT = default; ++ ++ ExternalFormatOHOS( VkExternalFormatOHOS const & rhs ) VULKAN_HPP_NOEXCEPT ++ : ExternalFormatOHOS( *reinterpret_cast( &rhs ) ) ++ { ++ } ++ ++ ExternalFormatOHOS & operator=( ExternalFormatOHOS const & rhs ) VULKAN_HPP_NOEXCEPT = default; ++# endif /*VULKAN_HPP_NO_STRUCT_CONSTRUCTORS*/ ++ ++ ExternalFormatOHOS & operator=( VkExternalFormatOHOS const & rhs ) VULKAN_HPP_NOEXCEPT ++ { ++ *this = *reinterpret_cast( &rhs ); ++ return *this; ++ } ++ ++# if !defined( VULKAN_HPP_NO_STRUCT_SETTERS ) ++ VULKAN_HPP_CONSTEXPR_14 ExternalFormatOHOS & setPNext( void * pNext_ ) VULKAN_HPP_NOEXCEPT ++ { ++ pNext = pNext_; ++ return *this; ++ } ++ ++ VULKAN_HPP_CONSTEXPR_14 ExternalFormatOHOS & setExternalFormat( uint64_t externalFormat_ ) VULKAN_HPP_NOEXCEPT ++ { ++ externalFormat = externalFormat_; ++ return *this; ++ } ++# endif /*VULKAN_HPP_NO_STRUCT_SETTERS*/ ++ ++ operator VkExternalFormatOHOS const &() const VULKAN_HPP_NOEXCEPT ++ { ++ return *reinterpret_cast( this ); ++ } ++ ++ operator VkExternalFormatOHOS &() VULKAN_HPP_NOEXCEPT ++ { ++ return *reinterpret_cast( this ); ++ } ++ ++# if defined( VULKAN_HPP_USE_REFLECT ) ++# if 14 <= VULKAN_HPP_CPP_VERSION ++ auto ++# else ++ std::tuple ++# endif ++ reflect() const VULKAN_HPP_NOEXCEPT ++ { ++ return std::tie( sType, pNext, externalFormat ); ++ } ++# endif ++ ++# if defined( VULKAN_HPP_HAS_SPACESHIP_OPERATOR ) ++ auto operator<=>( ExternalFormatOHOS const & ) const = default; ++# else ++ bool operator==( ExternalFormatOHOS const & rhs ) const VULKAN_HPP_NOEXCEPT ++ { ++# if defined( VULKAN_HPP_USE_REFLECT ) ++ return this->reflect() == rhs.reflect(); ++# else ++ return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( externalFormat == rhs.externalFormat ); ++# endif ++ } ++ ++ bool operator!=( ExternalFormatOHOS const & rhs ) const VULKAN_HPP_NOEXCEPT ++ { ++ return !operator==( rhs ); ++ } ++# endif ++ ++ public: ++ VULKAN_HPP_NAMESPACE::StructureType sType = StructureType::eExternalFormatOHOS; ++ void * pNext = {}; ++ uint64_t externalFormat = {}; ++ }; ++ ++ template <> ++ struct CppType ++ { ++ using Type = ExternalFormatOHOS; ++ }; ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ ++ + #if defined( VK_USE_PLATFORM_SCREEN_QNX ) + struct ExternalFormatQNX + { +@@ -50926,6 +51243,105 @@ namespace VULKAN_HPP_NAMESPACE + }; + #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ struct MemoryGetNativeBufferInfoOHOS ++ { ++ using NativeType = VkMemoryGetNativeBufferInfoOHOS; ++ ++ static const bool allowDuplicate = false; ++ static VULKAN_HPP_CONST_OR_CONSTEXPR StructureType structureType = StructureType::eMemoryGetNativeBufferInfoOHOS; ++ ++# if !defined( VULKAN_HPP_NO_STRUCT_CONSTRUCTORS ) ++ VULKAN_HPP_CONSTEXPR MemoryGetNativeBufferInfoOHOS( VULKAN_HPP_NAMESPACE::DeviceMemory memory_ = {}, ++ const void * pNext_ = nullptr ) VULKAN_HPP_NOEXCEPT ++ : pNext( pNext_ ) ++ , memory( memory_ ) ++ { ++ } ++ ++ VULKAN_HPP_CONSTEXPR MemoryGetNativeBufferInfoOHOS( MemoryGetNativeBufferInfoOHOS const & rhs ) VULKAN_HPP_NOEXCEPT = default; ++ ++ MemoryGetNativeBufferInfoOHOS( VkMemoryGetNativeBufferInfoOHOS const & rhs ) VULKAN_HPP_NOEXCEPT ++ : MemoryGetNativeBufferInfoOHOS( *reinterpret_cast( &rhs ) ) ++ { ++ } ++ ++ MemoryGetNativeBufferInfoOHOS & operator=( MemoryGetNativeBufferInfoOHOS const & rhs ) VULKAN_HPP_NOEXCEPT = default; ++# endif /*VULKAN_HPP_NO_STRUCT_CONSTRUCTORS*/ ++ ++ MemoryGetNativeBufferInfoOHOS & operator=( VkMemoryGetNativeBufferInfoOHOS const & rhs ) VULKAN_HPP_NOEXCEPT ++ { ++ *this = *reinterpret_cast( &rhs ); ++ return *this; ++ } ++ ++# if !defined( VULKAN_HPP_NO_STRUCT_SETTERS ) ++ VULKAN_HPP_CONSTEXPR_14 MemoryGetNativeBufferInfoOHOS & setPNext( const void * pNext_ ) VULKAN_HPP_NOEXCEPT ++ { ++ pNext = pNext_; ++ return *this; ++ } ++ ++ VULKAN_HPP_CONSTEXPR_14 MemoryGetNativeBufferInfoOHOS & setMemory( VULKAN_HPP_NAMESPACE::DeviceMemory memory_ ) VULKAN_HPP_NOEXCEPT ++ { ++ memory = memory_; ++ return *this; ++ } ++# endif /*VULKAN_HPP_NO_STRUCT_SETTERS*/ ++ ++ operator VkMemoryGetNativeBufferInfoOHOS const &() const VULKAN_HPP_NOEXCEPT ++ { ++ return *reinterpret_cast( this ); ++ } ++ ++ operator VkMemoryGetNativeBufferInfoOHOS &() VULKAN_HPP_NOEXCEPT ++ { ++ return *reinterpret_cast( this ); ++ } ++ ++# if defined( VULKAN_HPP_USE_REFLECT ) ++# if 14 <= VULKAN_HPP_CPP_VERSION ++ auto ++# else ++ std::tuple ++# endif ++ reflect() const VULKAN_HPP_NOEXCEPT ++ { ++ return std::tie( sType, pNext, memory ); ++ } ++# endif ++ ++# if defined( VULKAN_HPP_HAS_SPACESHIP_OPERATOR ) ++ auto operator<=>( MemoryGetNativeBufferInfoOHOS const & ) const = default; ++# else ++ bool operator==( MemoryGetNativeBufferInfoOHOS const & rhs ) const VULKAN_HPP_NOEXCEPT ++ { ++# if defined( VULKAN_HPP_USE_REFLECT ) ++ return this->reflect() == rhs.reflect(); ++# else ++ return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( memory == rhs.memory ); ++# endif ++ } ++ ++ bool operator!=( MemoryGetNativeBufferInfoOHOS const & rhs ) const VULKAN_HPP_NOEXCEPT ++ { ++ return !operator==( rhs ); ++ } ++# endif ++ ++ public: ++ VULKAN_HPP_NAMESPACE::StructureType sType = StructureType::eMemoryGetNativeBufferInfoOHOS; ++ const void * pNext = {}; ++ VULKAN_HPP_NAMESPACE::DeviceMemory memory = {}; ++ }; ++ ++ template <> ++ struct CppType ++ { ++ using Type = MemoryGetNativeBufferInfoOHOS; ++ }; ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ ++ + struct MemoryGetFdInfoKHR + { + using NativeType = VkMemoryGetFdInfoKHR; +diff --git a/include/vulkan/vulkan_to_string.hpp b/include/vulkan/vulkan_to_string.hpp +index b7c08c7..20f9efd 100644 +--- a/include/vulkan/vulkan_to_string.hpp ++++ b/include/vulkan/vulkan_to_string.hpp +@@ -4008,6 +4008,11 @@ namespace VULKAN_HPP_NAMESPACE + case StructureType::eExternalFormatANDROID: return "ExternalFormatANDROID"; + case StructureType::eAndroidHardwareBufferFormatProperties2ANDROID: return "AndroidHardwareBufferFormatProperties2ANDROID"; + #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ ++#if defined( VK_USE_PLATFORM_OHOS_KHR ) ++ case StructureType::eExternalFormatOHOS: return "ExternalFormatOHOS"; ++ case StructureType::eNativeBufferPropertiesOHOS: return "NativeBufferPropertiesOHOS"; ++ case StructureType::eNativeBufferFormatPropertiesOHOS: return "NativeBufferFormatPropertiesOHOS"; ++#endif /*VK_USE_PLATFORM_OHOS_KHR*/ + #if defined( VK_ENABLE_BETA_EXTENSIONS ) + case StructureType::ePhysicalDeviceShaderEnqueueFeaturesAMDX: return "PhysicalDeviceShaderEnqueueFeaturesAMDX"; + case StructureType::ePhysicalDeviceShaderEnqueuePropertiesAMDX: return "PhysicalDeviceShaderEnqueuePropertiesAMDX"; diff --git a/attachment/repos/zlib.patch b/attachment/repos/zlib.patch new file mode 100644 index 0000000000000000000000000000000000000000..a7354c878f826bc3376a9eb87ec6c1d7360c541a --- /dev/null +++ b/attachment/repos/zlib.patch @@ -0,0 +1,13 @@ +diff --git a/BUILD.gn b/BUILD.gn +index 3a71693..dfd052c 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -119,7 +119,7 @@ if (use_arm_neon_optimizations) { + defines = [ "CRC32_ARMV8_CRC32" ] + if (is_android) { + defines += [ "ARMV8_OS_ANDROID" ] +- } else if (is_linux || is_chromeos) { ++ } else if (is_linux || is_chromeos || is_ohos) { + defines += [ "ARMV8_OS_LINUX" ] + } else if (is_mac) { + defines += [ "ARMV8_OS_MACOS" ] diff --git a/attachment/scripts/__init__.py b/attachment/scripts/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b77650f002ba0e7f9ee77b4d2ce1ec4baeeaada9 --- /dev/null +++ b/attachment/scripts/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +# 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. \ No newline at end of file diff --git a/attachment/scripts/artifacts_files.json b/attachment/scripts/artifacts_files.json new file mode 100644 index 0000000000000000000000000000000000000000..75b808ca05174f29e6f2aef35a5898ba5d562d6c --- /dev/null +++ b/attachment/scripts/artifacts_files.json @@ -0,0 +1,63 @@ +{ + "darwin-arm64": { + "src/out/host_release/gen/flutter/build/archives/LICENSE.artifacts.md": "LICENSE.artifacts.md", + "src/flutter/lib/gpu/": "flutter_gpu", + "src/out/host_release/frontend_server.dart.snapshot": "frontend_server.dart.snapshot", + "src/out/host_release/gen_snapshot": "gen_snapshot", + "src/out/host_release/icudtl.dat": "icudtl.dat", + "src/out/host_release/impellerc": "impellerc", + "src/out/host_debug_unopt/gen/flutter/lib/snapshot/isolate_snapshot.bin": "isolate_snapshot.bin", + "src/out/host_release/libpath_ops.dylib": "libpath_ops.dylib", + "src/out/host_release/libtessellator.dylib": "libtessellator.dylib", + "src/out/host_release/shader_lib/": "shader_lib", + "src/out/host_debug_unopt/gen/flutter/lib/snapshot/vm_isolate_snapshot.bin": "vm_isolate_snapshot.bin", + "src/out/host_release/gen/const_finder.dart.snapshot": "const_finder.dart.snapshot" + + }, + "darwin-x64": { + "src/out/host_release/gen/flutter/build/archives/LICENSE.artifacts.md": "LICENSE.artifacts.md", + "src/flutter/lib/gpu/": "flutter_gpu", + "src/out/host_release/flutter_tester": "flutter_tester", + "src/out/host_release/frontend_server.dart.snapshot": "frontend_server.dart.snapshot", + "src/out/host_release/gen_snapshot": "gen_snapshot", + "src/out/host_release/icudtl.dat": "icudtl.dat", + "src/out/host_release/impellerc": "impellerc", + "src/out/host_debug_unopt/gen/flutter/lib/snapshot/isolate_snapshot.bin": "isolate_snapshot.bin", + "src/out/host_release/libpath_ops.dylib": "libpath_ops.dylib", + "src/out/host_release/libtessellator.dylib": "libtessellator.dylib", + "src/out/host_release/shader_lib/": "shader_lib", + "src/out/host_debug_unopt/gen/flutter/lib/snapshot/vm_isolate_snapshot.bin": "vm_isolate_snapshot.bin", + "src/out/host_release/gen/const_finder.dart.snapshot": "const_finder.dart.snapshot" + }, + "windows-x64": { + "src/out/host_release/gen/flutter/build/archives/LICENSE.artifacts.md": "LICENSE.artifacts.md", + "src/flutter/lib/gpu/": "flutter_gpu", + "src/out/host_release/flutter_tester.exe": "flutter_tester.exe", + "src/out/host_release/frontend_server.dart.snapshot": "frontend_server.dart.snapshot", + "src/out/host_release/gen_snapshot.exe": "gen_snapshot.exe", + "src/out/host_release/icudtl.dat": "icudtl.dat", + "src/out/host_release/impellerc.exe": "impellerc.exe", + "src/out/host_debug_unopt/gen/flutter/lib/snapshot/isolate_snapshot.bin": "isolate_snapshot.bin", + "src/out/host_release/path_ops.dll": "path_ops.dll", + "src/out/host_release/libtessellator.dll": "libtessellator.dll", + "src/out/host_release/shader_lib/": "shader_lib", + "src/out/host_debug_unopt/gen/flutter/lib/snapshot/vm_isolate_snapshot.bin": "vm_isolate_snapshot.bin", + "src/out/host_release/gen/const_finder.dart.snapshot": "const_finder.dart.snapshot" + }, + "linux-x64": { + "src/out/host_release/gen/flutter/build/archives/LICENSE.artifacts.md": "LICENSE.artifacts.md", + "src/flutter/lib/gpu/": "flutter_gpu", + "src/out/host_release/flutter_tester": "flutter_tester", + "src/out/host_release/frontend_server.dart.snapshot": "frontend_server.dart.snapshot", + "src/out/host_release/gen_snapshot": "gen_snapshot", + "src/out/host_release/icudtl.dat": "icudtl.dat", + "src/out/host_release/impellerc": "impellerc", + "src/out/host_debug_unopt/gen/flutter/lib/snapshot/isolate_snapshot.bin": "isolate_snapshot.bin", + "src/out/host_release/libpath_ops.so": "libpath_ops.so", + "src/out/host_release/libtessellator.so": "libtessellator.so", + "src/out/host_release/shader_lib/": "shader_lib", + "src/out/host_debug_unopt/gen/flutter/lib/snapshot/vm_isolate_snapshot.bin": "vm_isolate_snapshot.bin", + "src/out/host_release/gen/const_finder.dart.snapshot": "const_finder.dart.snapshot" + } +} + diff --git a/attachment/scripts/auto_compile.py b/attachment/scripts/auto_compile.py new file mode 100644 index 0000000000000000000000000000000000000000..124b912ae8feff4368219deb39cb242f44842a63 --- /dev/null +++ b/attachment/scripts/auto_compile.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import os +import platform +import subprocess +import sys +import traceback +import zipfile +import argparse +from pathlib import Path +from obs import ObsClient, PutObjectHeader + +# 使用华为obs +# pip install esdk-obs-python --trusted-host pypi.org + +OS_NAME = platform.system().lower() +IS_WINDOWS = OS_NAME.startswith("win") +IS_MAC = OS_NAME.startswith("darwin") +engine_project_root_dir = Path(os.path.realpath(__file__)).parents[4] +FLUTTER_ENGINE_PATH = os.path.join(engine_project_root_dir.__str__(), 'src', 'flutter') +OHOS_ENGINE_TYPE_OUT = { + 'ohos-arm64': 'ohos_debug_unopt_arm64', + 'ohos-arm64-profile': 'ohos_profile_arm64', + 'ohos-arm64-release': 'ohos_release_arm64', + 'ohos-x64': 'ohos_debug_unopt_x64', + 'ohos-x64-profile': 'ohos_profile_x64', + 'ohos-x64-release': 'ohos_release_x64', +} + +UPLOAD_TEMPLATE = "upload_template.json" + +# OBS 环境变量配置key信息 +ACCESS_KEY = os.getenv("AccessKeyID") +SECRET_KEY = os.getenv("SecretAccessKey") + +# 服务器地址 华南-广州 +SERVER = "https://obs.cn-south-1.myhuaweicloud.com" + +# OBS桶 +FLUTTER_OHOS = 'flutter-ohos' + + +def log(msg): + print(f'================{msg}============') + + +def runGitCommand(command): + result = subprocess.run(command, capture_output=True, text=True, shell=True) + if result.returncode != 0: + raise Exception(f"Git command failed: {result.stderr}") + return result.stdout.strip() + + +def runPyCommand(command): + + if IS_WINDOWS: + proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + else: + proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False) + + # 获取实时输出 + for line in iter(proc.stdout.readline, b''): + print(line.decode('utf-8').strip()) + + # 等待命令执行完成 + proc.wait() + + +def getRemoteTags(repoPath, remoteName='origin'): + + # 获取远程仓库的所有标签 + tagsOutput = runGitCommand(f'git -C {repoPath} ls-remote --tags {remoteName}') + + # 分割输出,每一行都是一个tag + tags = tagsOutput.split('\n') + + # 去除空行和解析tag信息 + parsedTags = [line.split()[0] for line in tags if line] + + return parsedTags + + +# 在指定目录及其子目录中查找指定的文件 +def findFile(directory, filename): + for root, dirs, files in os.walk(directory): + if filename in files: + return (os.path.join(root, filename)) + + +# 获取上传对象的进度 +def uploadCallback(transferredAmount, totalAmount, totalSeconds): + # 获取上传平均速率(KB/S) + speed = int(transferredAmount / totalSeconds / 1024) + # 获取上传进度百分比 + progress = int(transferredAmount * 100.0 / totalAmount) + print("\r", end="") + print("Speed: {} KB/S progress: {}%: ".format(speed, progress), "▓" * (progress // 2), end="") + sys.stdout.flush() + if progress == 100: + print("") + + +# 检查TAG版本 +def checkRemoteTagsUpdates(): + # 获取远程TAG的最新提交哈希 + remoteTags = getRemoteTags(FLUTTER_ENGINE_PATH) + remoteLatestTag = remoteTags[-1] + + # 获取本地分支的最新提交哈希 + localLatestCommit = runGitCommand(f'git -C {FLUTTER_ENGINE_PATH} rev-parse HEAD') + + if remoteLatestTag != localLatestCommit: + log("Remote repository has updates.") + return True + else: + log("Local repository is up to date.") + return False + + +# 检查分支更新 +def checkRemoteBranchUpdates(repoPath, remoteName='origin', branch='dev'): + # 切换到你的Git仓库目录 + os.chdir(repoPath) + + # 获取远程分支的最新提交哈希 + remoteLatestCommit = runGitCommand(f'git -C {repoPath} rev-parse {remoteName}/{branch}') + + # 获取本地分支的最新提交哈希 + localLatestCommit = runGitCommand(f'git -C {repoPath} rev-parse HEAD') + + if remoteLatestCommit != localLatestCommit: + log("Remote repository has updates.") + return True + else: + log("Local repository is up to date.") + return False + + +# 获取编译产物 +def getCompileFiles(buildType): + zipfiles = ['artifacts.zip', 'linux-x64.zip', 'windows-x64.zip', 'darwin-x64.zip', 'symbols.zip'] + + files = [] + for fileName in zipfiles: + if IS_WINDOWS and fileName != 'windows-x64.zip': + continue + if IS_MAC and fileName != 'darwin-x64.zip': + continue + + files.append( + findFile( + os.path.join(engine_project_root_dir.__str__(), 'src', 'out', buildType), fileName + ) + ) + + return files + + +def uploadExtraFiles(localVersion): + log("uploadExtraFiles ") + if not os.path.exists(UPLOAD_TEMPLATE): + log(f"File not exist: {UPLOAD_TEMPLATE}") + return + with open(UPLOAD_TEMPLATE, 'r') as jsonFile: + jsonData = json.load(jsonFile) + tag = jsonData["tag"] + if not tag: + tag = localVersion + log(f"tag = {tag}") + uploadFiles = jsonData["files"] + for key in uploadFiles: + value = uploadFiles[key] + if os.path.exists(value): + uploadServer(tag, key, value) + else: + log(f"Not exist file: {value}") + + +# 上传服务器 +def uploadServer(version, buildType, filePath): + try: + log(f'upload: {filePath}') + + bucketName = FLUTTER_OHOS + obsClient = ObsClient(access_key_id=ACCESS_KEY, secret_access_key=SECRET_KEY, server=SERVER) + + # 上传对象的附加头域 + headers = PutObjectHeader() + # 【可选】待上传对象的MIME类型 + headers.contentType = 'text/plain' + + # https://storage.flutter-io.cn/flutter_infra_release/flutter/cececddab019a56da828c41d55cb54484278e880/ohos-arm64-profile/linux-x64.zip + fileName = os.path.basename(filePath) + objectKey = f'flutter_infra_release/flutter/{version}/{buildType}/{fileName}' + log(f'objectKey: {objectKey}') + + # 待上传文件/文件夹的完整路径,如aa/bb.txt,或aa/ + file_path = filePath + + # 文件上传 + resp = obsClient.putFile( + bucketName, objectKey, file_path, headers=headers, progressCallback=uploadCallback + ) + # 返回码为2xx时,接口调用成功,否则接口调用失败 + if resp.status < 300: + print('Put Content Succeeded') + print('requestId:', resp.requestId) + print('etag:', resp.body.etag) + else: + print('Put Content Failed') + print('requestId:', resp.requestId) + print('errorCode:', resp.errorCode) + print('errorMessage:', resp.errorMessage) + + except: + log('Put Content Failed') + log(traceback.format_exc()) + + +def main(): + if checkRemoteTagsUpdates(): + parser = argparse.ArgumentParser( + prog='auto_compile', description='upload artifacts', epilog='upload artifacts' + ) + parser.add_argument('-t', '--tag') + args = parser.parse_args() + if args.tag: + version = args.tag + else: + version = runGitCommand(f'git -C {FLUTTER_ENGINE_PATH} rev-parse HEAD') + log(version) + # 获取编译产物 + for buildType in OHOS_ENGINE_TYPE_OUT: + zipfiles = getCompileFiles(OHOS_ENGINE_TYPE_OUT[buildType]) + for filePath in zipfiles: + if not filePath or not os.path.exists(filePath): + continue + uploadServer(version, buildType, filePath) + + uploadExtraFiles(version) + log('上传完成') + + else: + log("本地代码已经是最新") + + +if __name__ == "__main__": + exit(main()) diff --git a/attachment/scripts/config.json b/attachment/scripts/config.json new file mode 100644 index 0000000000000000000000000000000000000000..3216c96f9f3b940f02a39a744d1cce5f201563de --- /dev/null +++ b/attachment/scripts/config.json @@ -0,0 +1,139 @@ +[ + { + "name": "bootstrap", + "target": "./", + "type": "files" + }, + { + "name": "dart3.4", + "target": "./src/third_party/dart", + "type": "patch", + "file_path": "../../flutter/attachment/repos/dart.3.4.0.patch" + }, + { + "name": "dart_debug", + "target": "./src/third_party/dart", + "type": "patch", + "file_path": "../../flutter/attachment/repos/dart.debug_tmp.patch" + }, + { + "name": "dart_debug1", + "target": "./src/third_party/dart", + "type": "patch", + "file_path": "../../flutter/attachment/repos/dart.debug_tmp1.patch" + }, + { + "name": "dart4", + "target": "./src/third_party/dart", + "type": "patch", + "file_path": "../../flutter/attachment/repos/dart.patch4" + }, + { + "name": "dart5", + "target": "./src/third_party/dart", + "type": "patch", + "file_path": "../../flutter/attachment/repos/dart.patch5" + }, + { + "name": "dart6", + "target": "./src/third_party/dart", + "type": "patch", + "file_path": "../../flutter/attachment/repos/dart.patch6" + }, + { + "name": "skia", + "target": "./src/flutter/third_party/skia", + "type": "patch", + "file_path": "../../../flutter/attachment/repos/skia-3.22.patch" + }, + { + "name": "skia_font_reload", + "target": "./src/flutter/third_party/skia", + "type": "patch", + "file_path": "../../../flutter/attachment/repos/skia-3.22.patch1" + }, + { + "name": "skia-2", + "target": "./src/flutter/third_party/skia", + "type": "patch", + "file_path": "../../../flutter/attachment/repos/skia-3.22.patch2" + }, + { + "name": "skia-3", + "target": "./src/flutter/third_party/skia", + "type": "patch", + "file_path": "../../../flutter/attachment/repos/skia-3.22.patch3" + }, + { + "name": "spirv-headers", + "target": "./src/flutter/third_party/vulkan-deps/spirv-headers/src", + "type": "patch", + "file_path": "../../../../../flutter/attachment/repos/spirv-headers.patch" + }, + { + "name": "angle", + "target": "./src/flutter/third_party/angle", + "type": "patch", + "file_path": "../../../flutter/attachment/repos/angle-3.22.patch" + }, + { + "name": "build", + "target": "./src/build", + "type": "patch", + "file_path": "../flutter/attachment/repos/build-3.22.patch" + }, + { + "name": "build2", + "target": "./src/build", + "type": "patch", + "file_path": "../flutter/attachment/repos/build-3.22.patch2" + }, + { + "name": "zlib", + "target": "./src/third_party/zlib", + "type": "patch", + "file_path": "../../flutter/attachment/repos/zlib.patch" + }, + { + "name": "boringssl", + "target": "./src/flutter/third_party/boringssl", + "type": "patch", + "file_path": "../../../flutter/attachment/repos/boringssl-3.22.patch" + }, + { + "name": "swiftshader", + "target": "./src/flutter/third_party/swiftshader", + "type": "patch", + "file_path": "../../../flutter/attachment/repos/swiftshader-3.22.patch" + }, + { + "name": "native", + "target": "./src/third_party/dart/third_party/pkg/native", + "type": "patch", + "file_path": "../../../../../flutter/attachment/repos/native.patch" + }, + { + "name": "vulkan", + "target": "./src/flutter/third_party/vulkan-deps/vulkan-headers/src", + "type": "patch", + "file_path": "../../../../../flutter/attachment/repos/vulkan.patch" + }, + { + "name": "libcxxabi", + "target": "./src/third_party/libcxxabi", + "type": "patch", + "file_path": "../../flutter/attachment/repos/libcxxabi.patch" + }, + { + "name": "libcxxabi2", + "target": "./src/third_party/libcxxabi", + "type": "patch", + "file_path": "../../flutter/attachment/repos/libcxxabi.patch2" + }, + { + "name": "libcxx", + "target": "./src/third_party/libcxx", + "type": "patch", + "file_path": "../../flutter/attachment/repos/libcxx.patch" + } +] diff --git a/attachment/scripts/config_pre.json b/attachment/scripts/config_pre.json new file mode 100644 index 0000000000000000000000000000000000000000..d3c555f0187fdcd7eb2a904bcabc1f9c060d20df --- /dev/null +++ b/attachment/scripts/config_pre.json @@ -0,0 +1,133 @@ +[ + { + "name": "bootstrap", + "target": "./", + "type": "files" + }, + { + "name": "dart3.4", + "target": "./src/third_party/dart", + "type": "patch", + "file_path": "../../flutter/attachment/repos/dart.3.4.0.patch" + }, + { + "name": "dart_debug", + "target": "./src/third_party/dart", + "type": "patch", + "file_path": "../../flutter/attachment/repos/dart.debug_tmp.patch" + }, + { + "name": "dart_debug1", + "target": "./src/third_party/dart", + "type": "patch", + "file_path": "../../flutter/attachment/repos/dart.debug_tmp1.patch" + }, + { + "name": "dart4", + "target": "./src/third_party/dart", + "type": "patch", + "file_path": "../../flutter/attachment/repos/dart.patch4" + }, + { + "name": "dart5", + "target": "./src/third_party/dart", + "type": "patch", + "file_path": "../../flutter/attachment/repos/dart.patch5" + }, + { + "name": "dart6", + "target": "./src/third_party/dart", + "type": "patch", + "file_path": "../../flutter/attachment/repos/dart.patch6" + }, + { + "name": "skia", + "target": "./src/flutter/third_party/skia", + "type": "patch", + "file_path": "../../../flutter/attachment/repos/skia-3.22.patch" + }, + { + "name": "skia_font_reload", + "target": "./src/flutter/third_party/skia", + "type": "patch", + "file_path": "../../../flutter/attachment/repos/skia-3.22.patch1" + }, + { + "name": "skia-2", + "target": "./src/flutter/third_party/skia", + "type": "patch", + "file_path": "../../../flutter/attachment/repos/skia-3.22.patch2" + }, + { + "name": "spirv-headers", + "target": "./src/flutter/third_party/vulkan-deps/spirv-headers/src", + "type": "patch", + "file_path": "../../../../../flutter/attachment/repos/spirv-headers.patch" + }, + { + "name": "angle", + "target": "./src/flutter/third_party/angle", + "type": "patch", + "file_path": "../../../flutter/attachment/repos/angle-3.22.patch" + }, + { + "name": "build", + "target": "./src/build", + "type": "patch", + "file_path": "../flutter/attachment/repos/build-3.22.patch" + }, + { + "name": "build2", + "target": "./src/build", + "type": "patch", + "file_path": "../flutter/attachment/repos/build-3.22.patch2" + }, + { + "name": "zlib", + "target": "./src/third_party/zlib", + "type": "patch", + "file_path": "../../flutter/attachment/repos/zlib.patch" + }, + { + "name": "boringssl", + "target": "./src/flutter/third_party/boringssl", + "type": "patch", + "file_path": "../../../flutter/attachment/repos/boringssl-3.22.patch" + }, + { + "name": "swiftshader", + "target": "./src/flutter/third_party/swiftshader", + "type": "patch", + "file_path": "../../../flutter/attachment/repos/swiftshader-3.22.patch" + }, + { + "name": "native", + "target": "./src/third_party/dart/third_party/pkg/native", + "type": "patch", + "file_path": "../../../../../flutter/attachment/repos/native.patch" + }, + { + "name": "vulkan", + "target": "./src/flutter/third_party/vulkan-deps/vulkan-headers/src", + "type": "patch", + "file_path": "../../../../../flutter/attachment/repos/vulkan.patch" + }, + { + "name": "libcxxabi", + "target": "./src/third_party/libcxxabi", + "type": "patch", + "file_path": "../../flutter/attachment/repos/libcxxabi.patch" + }, + { + "name": "libcxxabi2", + "target": "./src/third_party/libcxxabi", + "type": "patch", + "file_path": "../../flutter/attachment/repos/libcxxabi.patch2" + }, + { + "name": "libcxx", + "target": "./src/third_party/libcxx", + "type": "patch", + "file_path": "../../flutter/attachment/repos/libcxx.patch" + } +] diff --git a/attachment/scripts/excute_util.py b/attachment/scripts/excute_util.py new file mode 100644 index 0000000000000000000000000000000000000000..17e7f28c7c1d7c4728b94c05171fdf09ba8e4318 --- /dev/null +++ b/attachment/scripts/excute_util.py @@ -0,0 +1,77 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +# 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. + +#!/usr/bin/python +import subprocess +from datetime import datetime + +# 执行字符串命令行 + + +def excute(cmdStr, path=".", log=True): + if path != "." and log: + print("下个指令执行目录:"+path) + start_time = datetime.now() + result = "" + err = False + if log: + print("开始执行命令:{} ,开始时间:{}".format(cmdStr, getDateStr(start_time))) + try: + proc = subprocess.Popen(cmdStr, cwd=path, shell=True, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + out, err = proc.communicate() + if proc.returncode != 0: + result = '-1' + if (log): + print("Executing: %s failed. Exit code: %s" % (cmdStr, proc.returncode)) + + except subprocess.CalledProcessError as e: + if log: + print("命令执行错误:", str(e)) + result = '-1' + except Exception as e: + if log: + print("其他异常:", str(e)) + result = '-1' + # 等待子进程结束 + proc.wait() + end_time = datetime.now() + if log: + print("执行命令结束:{} ,结束时间:{} ,任务耗时: {}".format( + cmdStr, getDateStr(end_time), end_time - start_time)) + return result + +# 执行数组命令 +def excuteArr(cmdArr, path=".", log = True): + str = ' '.join(cmdArr) + return excute(str, path, log) + +def getDateStr(date): + hour = date.hour + minute = date.minute + second = date.second + return "{}时{}分{}秒".format(hour, minute, second) + + +# 测试代码 +def testExcuteStr(): + cmdStr = "ls" + excute(cmdStr, path="/") + + +def testExcuteArr(): + cmdArr = ["ping", "baidu.com"] + excuteArr(cmdArr) + +# testExcuteStr() +# testExcuteArr() diff --git a/attachment/scripts/file_util.py b/attachment/scripts/file_util.py new file mode 100644 index 0000000000000000000000000000000000000000..28d779a6929a84914da322b3ca6d44525322360c --- /dev/null +++ b/attachment/scripts/file_util.py @@ -0,0 +1,72 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +# 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. + +#!/usr/bin/python +import shutil +import os + + +def copytree(src, dst, symlinks=False, ignore=None): + if not os.path.exists(dst): + os.makedirs(dst) + for item in os.listdir(src): + s = os.path.join(src, item) + d = os.path.join(dst, item) + + if os.path.islink(s): + if symlinks: + linkto = os.readlink(s) + os.symlink(linkto, d) + else: + pass + elif os.path.isdir(s): + copytree(s, d, symlinks, ignore) + else: + copy_file(s, d) + + +def copy_file(sourceFile, targetFile, log=False): + if log: + print("拷贝文件,从{}到{}".format(sourceFile, targetFile)) + if os.path.exists(targetFile): + os.remove(targetFile) + shutil.copy2(sourceFile, targetFile) + + +# 拷贝文件夹 +def copy_dir(sourceFiles, targetFiles, log=False): + if log: + print("拷贝文件夹,从{}到{}".format(sourceFiles, targetFiles)) + if os.path.exists(targetFiles): + print("目标文件夹已存在,退出") + pass + # shutil.rmtree(targetFiles) + else: + copytree(sourceFiles, targetFiles) + + +# 拷贝文件夹中所有文件到目标目录(必须存在),不拷贝文件夹本身 +def copy_files(source_folder, destination_folder, log=False): + if log: + print("拷贝文件夹内所有文件,从{}到{}".format(source_folder, destination_folder)) + # 获取源文件夹中的所有文件 + files = os.listdir(source_folder) + + # 遍历文件并拷贝到目标文件夹 + for file_name in files: + source_file = os.path.join(source_folder, file_name) + destination_file = os.path.join(destination_folder, file_name) + if os.path.isfile(source_file) and os.path.exists(source_file) and not os.path.islink(source_file): + copy_file(source_file, destination_file) + elif os.path.isdir(source_file) and os.path.exists(source_file): + copy_dir(source_file, destination_file) diff --git a/attachment/scripts/ohos.py b/attachment/scripts/ohos.py new file mode 100644 index 0000000000000000000000000000000000000000..33f23c1f73af29e093df6d642c8a7f25b3ef337d --- /dev/null +++ b/attachment/scripts/ohos.py @@ -0,0 +1,401 @@ +#!/usr/bin/env python3 +# coding=utf-8 +# +# Copyright (c) 2021-2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import json +import logging +import os +import platform +import re +import shutil +import subprocess +import sys +import zipfile +from datetime import datetime + +SUPPORT_BUILD_NAMES = ("clean", "config", "har", "compile", "zip", "zip2", "upload") +SUPPORT_BUILD_TYPES = ("debug", "profile", "release") +DIR_ROOT = os.path.abspath(os.path.join(sys.argv[0], os.pardir)) +OS_NAME = platform.system().lower() +IS_WINDOWS = OS_NAME.startswith("win") +PATH_SEP = ";" if IS_WINDOWS else ":" +logging.basicConfig( + format="%(levelname)s:%(asctime)s: %(message)s", + datefmt="%Y-%d-%m %H:%M:%S", + level=logging.DEBUG, +) + + +def safeGetPath(filePath, isDirectory=True): + createPath = (filePath if isDirectory else os.path.abspath(os.path.join(filePath, os.pardir))) + if not os.path.exists(createPath): + os.makedirs(createPath) + return os.path.abspath(filePath) + + +TIME_STR = datetime.now().strftime("%Y%m%d-%H%M") +DIR_OUTPUTS = safeGetPath("%s/outputs/%s" % (DIR_ROOT, datetime.now().strftime("%Y%m%d"))) +DIR_LOG = safeGetPath("%s/logs" % DIR_OUTPUTS) + + +class BuildInfo: + + def __init__( + self, + buildType="release", + targetOS="ohos", + targetArch="arm64", + targetTriple="arm64-%s-ohos" % OS_NAME, + abi="arm64-v8a", + ): + self.buildType = buildType + self.targetOS = targetOS + self.targetArch = targetArch + self.targetTriple = targetTriple + self.abi = abi + + def __repr__(self): + return "BuildInfo(buildType=%s)" % (self.buildType) + + +# 执行命令 +def runCommand(command, checkCode=True, timeout=None): + logging.info("runCommand start, command = %s" % (command)) + code = subprocess.Popen(command, shell=True).wait(timeout) + + if code != 0: + logging.error("runCommand error, code = %s, command = %s" % (code, command)) + if checkCode: + exit(code) + else: + logging.info("runCommand finish, code = %s, command = %s" % (code, command)) + + +def getOutput(buildInfo): + buildType = buildInfo.buildType + outputName = "%s_%s%s_%s" % ( + buildInfo.targetOS, + buildType, + "_unopt" if buildType == "debug" else "", + buildInfo.targetArch, + ) + return outputName + + +# 清理engine产物 +def engineClean(buildInfo): + target = os.path.join("src", "out", getOutput(buildInfo)) + logging.info("Remove directory %s" % target) + if os.path.exists(target): + shutil.rmtree(target, ignore_errors=True) + + +def findFile(path, search, results): + if not path or not os.path.exists(path): + return + for item in os.listdir(path): + cur_path = os.path.join(path, item) + if os.path.isdir(cur_path): + if cur_path.endswith(search): + results.append(os.path.abspath(cur_path)) + findFile(cur_path, search, results) + + +def findNativeInCurrentDir(): + os_dir = "mac" if OS_NAME == "darwin" else OS_NAME + dirs = [] + findFile(os.path.join("ndk", os_dir), "native", dirs) + return dirs[0] if len(dirs) != 0 else "" + + +def getNdkHome(): + OHOS_NDK_HOME = os.getenv("OHOS_NDK_HOME") + if not OHOS_NDK_HOME: + OHOS_NDK_HOME = findNativeInCurrentDir() + if not OHOS_NDK_HOME: + dirs = [] + findFile(os.getenv("OHOS_SDK_HOME"), "native", dirs) + findFile(os.getenv("HOS_SDK_HOME"), "native", dirs) + dirs.sort(reverse=True) + for dir in dirs: + if isNdkValid(dir): + OHOS_NDK_HOME = dir + break + logging.info("OHOS_NDK_HOME = %s" % OHOS_NDK_HOME) + if not isNdkValid(OHOS_NDK_HOME): + logging.error( + """ + Please set the environment variables for HarmonyOS SDK to "HOS_SDK_HOME" or "OHOS_SDK_HOME". + We will use both native/llvm and native/sysroot. + Please ensure that the file "native/llvm/bin/clang" exists and is executable.""" + ) + exit(10) + return OHOS_NDK_HOME + + +# 校验 native +def isNdkValid(path): + if not path: + return False + dirs = [ + os.path.join(path), + os.path.join(path, "sysroot"), + os.path.join(path, "llvm", "bin"), + os.path.join(path, "build-tools", "cmake", "bin"), + ] + for dir in dirs: + if not os.path.exists(dir): + return False + return True + + +# 指定engine编译的配置参数 +def engineConfig(buildInfo, args): + OHOS_NDK_HOME = getNdkHome() + # export PATH=$OHOS_NDK_HOME/build-tools/cmake/bin:$OHOS_NDK_HOME/llvm/bin:$PATH + lastPath = os.getenv("PATH") + os.environ["PATH"] = ( + "%s%s" % (os.path.join(OHOS_NDK_HOME, "build-tools", "cmake", "bin"), PATH_SEP) + "%s%s" % + (os.path.join(OHOS_NDK_HOME, "build-tools", "llvm", "bin"), PATH_SEP) + "%s%s" % + (os.path.abspath("depot_tools"), PATH_SEP) + lastPath + ) + unixCommand = "" + if not IS_WINDOWS: + unixCommand = ( + "--target-sysroot %s " % os.path.join(OHOS_NDK_HOME, "sysroot") + + "--target-toolchain %s " % os.path.join(OHOS_NDK_HOME, "llvm") + + "--target-triple %s " % buildInfo.targetTriple + ) + OPT = "--unoptimized --no-lto " if buildInfo.buildType == "debug" else "" + runCommand( + "%s " % os.path.join("src", "flutter", "tools", "gn") + "--ohos " + + "--ohos-cpu %s " % buildInfo.targetArch + "--runtime-mode %s " % buildInfo.buildType + OPT + + unixCommand + "--no-goma " + "--no-prebuilt-dart-sdk " + "--full-dart-sdk " + + "--embedder-for-target " + "--disable-desktop-embeddings " + "--no-build-embedder-examples " + + "--ohos-api-int %s " % args.ohos_api_int + "--verbose " + + args.gn_extra_param.replace("\\", ""), + checkCode=False, + timeout=600, + ) + + +# 执行engine编译操作 +def engineCompile(buildInfo): + command = "ninja -C %s default " % os.path.join("src", "out", getOutput(buildInfo)) + if IS_WINDOWS and buildInfo.buildType != "debug": + command += "flutter/build/archives:archive_win_gen_snapshot " + runCommand(command) + + +# 编译har文件 +def harBuild(buildInfo, args): + buildType = buildInfo.buildType + buildOut = getOutput(buildInfo) + command = "python3 ./src/flutter/attachment/scripts/ohos_create_flutter_har.py " + command += "--embedding_src ./src/flutter/shell/platform/ohos/flutter_embedding " + command += "--build_dir ./src/out/%s/obj/ohos/flutter_embedding " % buildOut + command += "--build_type %s " % buildType + command += "--output ./src/out/%s/flutter.har " % buildOut + if args.har_unstripped: + command += "--native_lib ./src/out/%s/so.unstripped/libflutter.so " % buildOut + else: + command += "--native_lib ./src/out/%s/libflutter.so " % buildOut + if buildType == "profile": + command += ( + "--native_lib ./src/out/%s/gen/flutter/shell/vmservice/ohos/libs/%s/libvmservice_snapshot.so " + % (buildOut, buildInfo.abi) + ) + command += "--ohos_abi %s " % buildInfo.abi + command += "--ohos_api_int %s " % args.ohos_api_int + runCommand(command) + + +def isPathValid(filepath, filename, includes, excludes): + fileOrigin = filepath + os.path.sep + filename + for regex in includes: + if re.search(regex, fileOrigin): + return True + if includes != []: + return False + for regex in excludes: + if re.search(regex, fileOrigin): + return False + return True + + +def zipFileDir( + fileIn, + fileName, + prefixInZip="", + includes=[], + excludes=[], + useZip2=False, +): + logging.info( + "fileIn= %s, fileName= %s, prefixInZip= %s, includes= %s, excludes= %s" % + (fileIn, fileName, prefixInZip, includes, excludes) + ) + fileOut1 = os.path.abspath("%s/%s.zip" % (DIR_OUTPUTS, fileName)) + fileOut2 = os.path.abspath("%s/%s-unstripped.zip" % (DIR_OUTPUTS, fileName)) + with zipfile.ZipFile(fileOut1, "w", zipfile.ZIP_DEFLATED) as zip1: + with zipfile.ZipFile(fileOut2, "w", zipfile.ZIP_DEFLATED) as zip2: + for path, dirnames, filenames in os.walk(fileIn): + fpath = path.replace(fileIn, "")[1:] + pPath = prefixInZip + os.sep + fpath + for filename in filenames: + path1 = os.path.join(path, filename) + path2 = os.path.join(pPath, filename) + if isPathValid(fpath, filename, includes, excludes): + zip1.write(path1, path2) + elif useZip2: + zip2.write(path1, path2) + + if not useZip2: + os.remove(fileOut2) + + +def zipFiles(buildInfo, useZip2=False, args=any): + logging.info("zipFiles buildInfo=%s" % buildInfo) + outputName = getOutput(buildInfo) + fileIn = os.path.abspath("%s/src/out/%s" % (DIR_ROOT, outputName)) + fileName = "ohos_%s_%s-%s-%s-%s" % ( + args.ohos_api_int, + buildInfo.buildType, + OS_NAME, + platform.machine(), + TIME_STR, + ) + prefixInZip = os.path.join("src", "out", outputName) + excludes = ["obj", "exe.unstripped", "so.unstripped"] + if IS_WINDOWS: + excludes.extend([".*\.ilk", ".*\.pdb"]) + zipFileDir(fileIn, fileName, prefixInZip, excludes=excludes, useZip2=useZip2) + + +def addParseParam(parser): + parser.add_argument( + "-n", + "--name", + nargs="+", + default=[], + choices=SUPPORT_BUILD_NAMES, + help="Provide build names.", + ) + parser.add_argument( + "-t", + "--type", + nargs="+", + default=SUPPORT_BUILD_TYPES, + choices=SUPPORT_BUILD_TYPES, + help="Provide build types.", + ) + parser.add_argument( + "-b", + "--branch", + help="Git branch name in src/flutter to update.", + ) + parser.add_argument( + "-g", + "--gn-extra-param", + nargs="?", + default="", + help='Extra param to src/flutter/tools/gn. Such as: -g "\\--enable-unittests"', + ) + parser.add_argument("--ohos_api_int", type=int, default=13, help="Ohos api int. Deprecated.") + parser.add_argument("--har-unstripped", action="store_true", help="Use so.unstripped or not.") + + +def updateCode(args): + if args.branch: + dir = os.path.join("src", "flutter") + runCommand("git -C %s add -A" % dir) + runCommand("git -C %s stash save 'Auto stash save.'" % dir) + runCommand("git -C %s checkout %s" % (dir, args.branch)) + runCommand("git -C %s pull --rebase" % dir, checkCode=False) + runCommand("git -C %s log -1" % dir) + runCommand("python3 src/flutter/attachment/scripts/ohos_setup.py") + + +def checkEnvironment(): + if not os.path.exists("src/flutter"): + logging.error( + "You should place the current file 'ohos.py'" + + " in the root directory of the 'engine' compilation." + ) + exit(1) + + +def buildLocalEngine(buildType, extraParam): + OPT = "--unoptimized --no-lto " if buildType == "debug" else "" + runCommand( + "%s " % os.path.join("src", "flutter", "tools", "gn") + "--runtime-mode %s " % buildType + + OPT + "--no-goma " + "--no-prebuilt-dart-sdk " + "--full-dart-sdk " + + "--disable-desktop-embeddings " + "--no-build-embedder-examples " + "--verbose " + + extraParam.replace("\\", ""), + checkCode=False, + timeout=600, + ) + outputName = "host_%s%s" % (buildType, "_unopt" if buildType == "debug" else "") + runCommand("ninja -C %s" % os.path.join("src", "out", outputName)) + + +def uploadFiles(): + runCommand("python3 src/flutter/attachment/scripts/auto_compile.py") + + +def buildByNameAndType(args): + buildNames = args.name if args.branch or args.name else ["config", "compile"] + buildTypes = args.type + for buildType in SUPPORT_BUILD_TYPES: + if not buildType in buildTypes: + continue + buildInfo = BuildInfo(buildType=buildType) + for buildName in SUPPORT_BUILD_NAMES: + if not buildName in buildNames: + continue + if "clean" == buildName: + engineClean(buildInfo) + elif "config" == buildName: + engineConfig(buildInfo, args) + elif "har" == buildName: + harBuild(buildInfo) + elif "compile" == buildName: + engineCompile(buildInfo) + elif "zip" == buildName: + zipFiles(buildInfo) + elif "zip2" == buildName: + zipFiles(buildInfo, True) + else: + logging.warning("Other name=%s" % buildName) + buildLocalEngine(buildType, args.gn_extra_param) + + if "upload" in buildNames: + uploadFiles() + + +def ohos_main(): + parser = argparse.ArgumentParser() + addParseParam(parser) + args = parser.parse_args() + checkEnvironment() + updateCode(args) + buildByNameAndType(args) + logging.info("ohos_main() finish.") + return 0 + + +if __name__ == "__main__": + exit(ohos_main()) diff --git a/attachment/scripts/ohos_create_flutter_har.py b/attachment/scripts/ohos_create_flutter_har.py new file mode 100644 index 0000000000000000000000000000000000000000..801c2673900c911bbdfe1a7924d6bd44045d7e46 --- /dev/null +++ b/attachment/scripts/ohos_create_flutter_har.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +# 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. + +"""Create a HAR incorporating all the components required to build a Flutter application""" + +import argparse +import logging +import os +import re +import shutil +import subprocess +import sys + + +def runGitCommand(command): + result = subprocess.run(command, capture_output=True, text=True, shell=True) + if result.returncode != 0: + raise Exception(f"Git command failed: {result.stderr}") + return result.stdout.strip() + + +# 自动更新flutter.har的版本号,把日期加到末尾。如: 1.0.0-20240731 +def updateVersion(buildDir): + filePath = os.path.join(buildDir, "flutter", "oh-package.json5") + currentDir = os.path.dirname(__file__) + latestCommit = runGitCommand(f'git -C {currentDir} rev-parse --short HEAD') + + with open(filePath, "r") as sources: + lines = sources.readlines() + + pattern = r"\d+\.(?:\d+\.)*\d+" + with open(filePath, "w") as sources: + for line in lines: + if "version" in line: + matches = re.findall(pattern, line) + print(f'matches = {matches}') + if matches and len(matches) > 0: + result = ''.join(matches[0]) + versionArr = result.split("-") + list = [versionArr[0], latestCommit] + versionStr = "-".join(list) + print(f'versionStr = {versionStr}') + sources.write(re.sub(pattern, versionStr, line)) + else: + sources.write(line) + else: + sources.write(line) + + +# 执行命令 +def runCommand(command, checkCode=True, timeout=None): + logging.info("runCommand start, command = %s" % (command)) + code = subprocess.Popen(command, shell=True).wait(timeout) + if code != 0: + logging.error("runCommand error, code = %s, command = %s" % (code, command)) + if checkCode: + exit(code) + else: + logging.info("runCommand finish, code = %s, command = %s" % (code, command)) + + +# 编译har文件,通过hvigorw的命令行参数指定编译类型(debug/release/profile) +def buildHar(buildDir, apiInt, buildType): + updateVersion(buildDir) + hvigorwCommand = "hvigorw" if apiInt != 11 else (".%shvigorw" % os.sep) + runCommand( + "cd %s && %s clean --mode module " % (buildDir, hvigorwCommand) + + "-p module=flutter@default -p product=default -p buildMode=%s " % buildType + + "assembleHar --no-daemon" + ) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--embedding_src", help="Path of embedding source code.") + parser.add_argument("--build_dir", help="Path to build.") + parser.add_argument( + "--build_type", + choices=["debug", "release", "profile"], + help="Type to build flutter.har.", + ) + parser.add_argument("--output", help="Path to output flutter.har.") + parser.add_argument("--native_lib", action="append", help="Native code library.") + parser.add_argument("--ohos_abi", help="Native code ABI.") + parser.add_argument("--ohos_api_int", type=int, default=13, help="Ohos api int. Deprecated.") + options = parser.parse_args() + # copy source code + if os.path.exists(options.build_dir): + shutil.rmtree(options.build_dir) + shutil.copytree(options.embedding_src, options.build_dir) + + # copy so files + for file in options.native_lib: + dir_name, full_file_name = os.path.split(file) + targetDir = os.path.join(options.build_dir, "flutter/libs", options.ohos_abi) + if not os.path.exists(targetDir): + os.makedirs(targetDir) + shutil.copyfile( + file, + os.path.join(targetDir, full_file_name), + ) + buildHar(options.build_dir, options.ohos_api_int, options.build_type) + shutil.copyfile( + os.path.join(options.build_dir, "flutter/build/default/outputs/default/flutter.har"), + options.output, + ) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/attachment/scripts/ohos_reverse_patch.py b/attachment/scripts/ohos_reverse_patch.py new file mode 100644 index 0000000000000000000000000000000000000000..c43013503816f47fdca3bbb1df549c0fee3848f5 --- /dev/null +++ b/attachment/scripts/ohos_reverse_patch.py @@ -0,0 +1,73 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +# 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. + +#!/usr/bin/python +import sys +import json +import sub_process_with_timeout +from operator import itemgetter + +""" +在gclient中pre_deps_hooks中配置执行,用于在sync前回滚patch +职责如下: +1.解析config.json,并按顺序倒序排序 +2.回滚目录下的patch +""" +ROOT = './src/flutter/attachment' + +def apply_reverse_patch(task, log=False): + file_path = task['file_path'] + target_path = task['target'] + retcode,stdout,stderr = sub_process_with_timeout.excuteArr( + ['git', 'apply', '-R', '--ignore-whitespace','--whitespace=nowarn', file_path], target_path, log, timeout=20) + if retcode == 0 and log: + print("Apply reverse succeded. file path:" + file_path) + if log: + print(str(stdout)) + print(str(stderr)) + if retcode != 0 and log: + print("Apply reverse failed. file path:" + file_path + " Error:" + str(stderr)) + pass + +def apply_reverse_check(task, log=False): + file_path = task['file_path'] + target_path = task['target'] + retcode,stdout,stderr = sub_process_with_timeout.excuteArr( + ['git', 'apply', '-R', '--check', '--ignore-whitespace', file_path], target_path, log, timeout=20) + if log: + print("retcode:" + str(retcode)) + print(str(stdout)) + print(str(stderr)) + if retcode != 0 and log: + print("Apply reverse check failed. file path:" + file_path + " Error:" + str(stderr)) + return retcode != '-1' and 'error' not in str(stderr) + +def doTask(task, log=False): + if (task['type'] == 'patch'): + if (apply_reverse_check(task, False)): + apply_reverse_patch(task, log) + +def parse_config(config_file="{}/scripts/config.json".format(ROOT)): + log = False + if (len(sys.argv) > 1): + if(sys.argv[1] == '-v'): + log = True + with open(config_file) as json_file: + data = json.load(json_file) + data = sorted(data, key=itemgetter('name'), reverse=True) + for task in data: + doTask(task, log) + +if __name__ == "__main__": + parse_config() + \ No newline at end of file diff --git a/attachment/scripts/ohos_setup.py b/attachment/scripts/ohos_setup.py new file mode 100644 index 0000000000000000000000000000000000000000..4bb80b246b906ead68898adc87b04260b965567f --- /dev/null +++ b/attachment/scripts/ohos_setup.py @@ -0,0 +1,102 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +# 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. + +#!/usr/bin/python +import sys +import json +import file_util +import sub_process_with_timeout +import os + +""" +在gclient中hook中配置执行 +职责如下: +1.解析config.json +2.拷贝repos下文件,覆盖执行路径目录 +3.修改DEPS,忽视dart、angle、skia的同步 +""" + +ROOT = './src/flutter/attachment' +REPOS_ROOT = ROOT + '/repos' + +def apply_patch(task, log=False): + file_path = task['file_path'] + target_path = task['target'] + retcode,stdout,stderr = sub_process_with_timeout.excuteArr( + ['git', 'apply', '--ignore-whitespace', '--whitespace=nowarn', file_path], target_path, log, timeout=20) + if retcode == 0 and log: + print("Apply succeded. file path:" + file_path) + if log: + print(str(stdout)) + print(str(stderr)) + if retcode != 0: + print("Apply failed. file path:" + file_path + " Error:" + str(stderr)) + pass + + +def stashChanges(task, log): + if task['type'] != 'patch': + return + target_path = task['target'] + sub_process_with_timeout.excuteArr([ + 'git', 'add', '-A' + ], target_path, log) + sub_process_with_timeout.excuteArr([ + 'git', 'stash', 'save', 'Auto stash by ohos_setup.py' + ], target_path, log) + + +def apply_check(task, log=False): + file_path = task['file_path'] + target_path = task['target'] + retcode,stdout,stderr = sub_process_with_timeout.excuteArr( + ['git', 'apply', '--check', '--ignore-whitespace', file_path], target_path, log, timeout=20) + if log: + print("retcode:" + str(retcode)) + print(str(stdout)) + print(str(stderr)) + if retcode != 0: + print("Apply check failed. file path:" + file_path + " Error:" + str(stderr)) + return retcode != -1 and 'error' not in str(stderr) + + +def doTask(task, log=False): + sourceFile = "{}/repos/{}".format(ROOT, task['name']) + targetFile = task['target'] + if (task['type'] == 'dir'): + file_util.copy_dir(sourceFile, targetFile, log) + elif (task['type'] == 'files'): + file_util.copy_files(sourceFile, targetFile, log) + elif (task['type'] == 'file'): + file_util.copy_file(sourceFile, targetFile, log) + elif (task['type'] == 'patch'): + if apply_check(task, log): + apply_patch(task, log) + pass + + +def parse_config(config_file="{}/scripts/config.json".format(ROOT), useStash=True): + log = False + if (len(sys.argv) > 1): + if(sys.argv[1] == '-v'): + log = True + with open(config_file) as json_file: + data = json.load(json_file) + if useStash: + for task in data: + stashChanges(task, log) + for task in data: + doTask(task, log) + +if __name__ == "__main__": + parse_config() diff --git a/attachment/scripts/ohos_setup_pre.py b/attachment/scripts/ohos_setup_pre.py new file mode 100644 index 0000000000000000000000000000000000000000..9d13a27facdc7e76e44cb44170f3946eb147173e --- /dev/null +++ b/attachment/scripts/ohos_setup_pre.py @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +# 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. + +#!/usr/bin/python +import ohos_setup + +# 在预编译脚本中使用的配置 +ohos_setup.parse_config("{}/scripts/config_pre.json".format(ohos_setup.ROOT), useStash=False) diff --git a/attachment/scripts/sub_process_with_timeout.py b/attachment/scripts/sub_process_with_timeout.py new file mode 100644 index 0000000000000000000000000000000000000000..36ab5b0bb01cc580df63802625a8b35f668d7f0a --- /dev/null +++ b/attachment/scripts/sub_process_with_timeout.py @@ -0,0 +1,74 @@ +import subprocess +import threading +import time +import os +import signal +import inspect +import ctypes +import tempfile + + +class TimeoutError(Exception): + pass + +class SubProcess(threading.Thread): + + def __init__(self, cmd, path, timeout=5, log = False): + super(SubProcess,self).__init__() + self.cmd = cmd + self.event = threading.Event() + self.stdout = "" + self.stderr = "" + self.retcode = None + self.timeout = timeout + self.path = path + self.log = log + self.sub = None + + def run(self): + try: + start_time = time.time() + read_flag = end_flag = False + PIPE = subprocess.PIPE + self.sub = subprocess.Popen(self.cmd, cwd=self.path, shell=True, stderr=PIPE, stdout=PIPE, text=True) + while not self.event.is_set() and self.sub.poll() is None: + self.stdout += self.sub.stdout.read() + time.sleep(0.05) + self.stderr +=self.sub.stderr.read() + if (time.time()-start_time) > self.timeout: + self.retcode = -2 + self.sub.stderr = TimeoutError('执行超时:' + self.cmd) + return + self.sub.stdout.close() + self.sub.stderr.close() + self.retcode = self.sub.returncode + except Exception as ex: + self.retcode = -1 + self.stderr = ex + self.event.set() + self.sub.stdout.close() + self.sub.stderr.close() + + def stop(self): + self.event.set() + +# 执行数组命令 +def excuteArr(cmdArr, path=".", log = True, timeout=10): + str = ' '.join(cmdArr) + return execCommandEx(str, path, log, timeout) + +def execCommandEx(cmd, path = ".", log = False, timeout=10): + start_time = end_time = time.time() + sub = SubProcess(cmd, path, 10, log) + sub.daemon = True + sub.start() + while True: + if int(time.time() - start_time) > int(timeout): + sub.retcode = -1 + sub.stderr = TimeoutError('执行超时:' + cmd) + sub.stop() + if not sub.is_alive() or sub.event.is_set(): + break + time.sleep(0.1) + end_time = time.time() + return sub.retcode,sub.stdout,sub.stderr diff --git a/attachment/scripts/upload_dart_sdk.py b/attachment/scripts/upload_dart_sdk.py new file mode 100644 index 0000000000000000000000000000000000000000..6f6164b378b11d9650506d49cef49783927046bd --- /dev/null +++ b/attachment/scripts/upload_dart_sdk.py @@ -0,0 +1,36 @@ +import os +import logging +import argparse +from pathlib import Path +from utils import getArch, getEnginePath, runGitCommand, upload + +engine_path = getEnginePath() + +logging.basicConfig() +log = logging.getLogger() +log.setLevel(logging.INFO) + + +def main(): + parser = argparse.ArgumentParser( + prog='upload_dart_sdk', description='upload dart sdk', epilog='upload dart sdk' + ) + parser.add_argument('-t', '--tag') + parser.add_argument('-f', '--file') + args = parser.parse_args() + file_name = 'dart-sdk-' + getArch() + '.zip' + if args.file: + file_path = args.file + else: + file_path = Path(engine_path).joinpath(file_name).__str__() + flutter_path = Path(engine_path).joinpath('src/flutter').__str__() + if args.tag: + version = args.tag + else: + version = runGitCommand(f'git -C {flutter_path} rev-parse HEAD') + log.info(version) + upload(version, file_path) + + +if __name__ == "__main__": + exit(main()) diff --git a/attachment/scripts/upload_flutter_patched_sdk.py b/attachment/scripts/upload_flutter_patched_sdk.py new file mode 100644 index 0000000000000000000000000000000000000000..25ed1a7936eacb15f11f35ca55fe3708afb6410c --- /dev/null +++ b/attachment/scripts/upload_flutter_patched_sdk.py @@ -0,0 +1,43 @@ +import logging +import argparse +from pathlib import Path +from utils import getEnginePath, runGitCommand +from utils import upload + +engine_path = getEnginePath() + +logging.basicConfig() +log = logging.getLogger() +log.setLevel(logging.INFO) + + +def main(): + parser = argparse.ArgumentParser( + prog='upload flutter patched sdk', + description='upload flutter_patched_sdk', + epilog='upload flutter_patched_sdk' + ) + parser.add_argument('-t', '--tag') + parser.add_argument('-d', '--patched_sdk') + parser.add_argument('-p', '--patched_sdk_product') + args = parser.parse_args() + patched_sdk = 'flutter_patched_sdk.zip' + patched_sdk_product = 'flutter_patched_sdk_product.zip' + if args.patched_sdk and args.patched_sdk_product: + patched_sdk_path = args.patched_sdk + patched_sdk_product_path = args.patched_sdk_product + else: + patched_sdk_path = Path(engine_path).joinpath(patched_sdk).__str__() + patched_sdk_product_path = Path(engine_path).joinpath(patched_sdk_product).__str__() + if args.tag: + version = args.tag + else: + flutter_path = Path(engine_path).joinpath('src/flutter').__str__() + version = runGitCommand(f'git -C {flutter_path} rev-parse HEAD') + log.info(version) + upload(version, patched_sdk_path) + upload(version, patched_sdk_product_path) + + +if __name__ == "__main__": + exit(main()) diff --git a/attachment/scripts/upload_sky_engine.py b/attachment/scripts/upload_sky_engine.py new file mode 100644 index 0000000000000000000000000000000000000000..f118921ce04862b7f91f67ae88e413476f10acef --- /dev/null +++ b/attachment/scripts/upload_sky_engine.py @@ -0,0 +1,36 @@ +import logging +import argparse +from pathlib import Path +from utils import getEnginePath, runGitCommand +from utils import upload + +engine_path = getEnginePath() + +logging.basicConfig() +log = logging.getLogger() +log.setLevel(logging.INFO) + + +def main(): + parser = argparse.ArgumentParser( + prog='upload sky_engine', description='upload sky_engine', epilog='upload sky_engine' + ) + parser.add_argument('-t', '--tag') + parser.add_argument('-f', '--file') + args = parser.parse_args() + file_name = 'sky_engine.zip' + if args.file: + file_path = args.file + else: + file_path = Path(engine_path).joinpath(file_name).__str__() + if args.tag: + version = args.tag + else: + flutter_path = Path(engine_path).joinpath('src/flutter').__str__() + version = runGitCommand(f'git -C {flutter_path} rev-parse HEAD') + log.info(version) + upload(version, file_path) + + +if __name__ == "__main__": + exit(main()) diff --git a/attachment/scripts/utils.py b/attachment/scripts/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..05de17b6d3749a1ecf2dec47dfecc8076231ca2f --- /dev/null +++ b/attachment/scripts/utils.py @@ -0,0 +1,78 @@ +import logging +import platform +import os +import subprocess +from pathlib import Path +import sys +from obs import ObsClient, PutObjectHeader + +log = logging.getLogger(__name__) + +FLUTTER_OHOS = 'flutter-ohos' + +# OBS 环境变量配置key信息 +ACCESS_KEY = os.getenv("AccessKeyID") +SECRET_KEY = os.getenv("SecretAccessKey") + +# 服务器地址 华南-广州 +SERVER = "https://obs.cn-south-1.myhuaweicloud.com" + + +def upload(version, file_path): + bucketName = FLUTTER_OHOS + obsClient = ObsClient(access_key_id=ACCESS_KEY, secret_access_key=SECRET_KEY, server=SERVER) + headers = PutObjectHeader() + headers.contentType = 'text/plain' + file_name = os.path.basename(file_path) + objectKey = f'flutter_infra_release/flutter/{version}/{file_name}' + print(f'objectKey: {objectKey}') + resp = obsClient.putFile( + bucketName, objectKey, file_path, headers=headers, progressCallback=uploadCallback + ) + if resp.status < 300: + print('Put Content Succeeded') + print('requestId:', resp.requestId) + print('etag:', resp.body.etag) + else: + print('Put Content Failed') + print('requestId:', resp.requestId) + print('errorCode:', resp.errorCode) + print('errorMessage:', resp.errorMessage) + + +def getEnginePath(): + return Path(os.path.realpath(__file__)).parents[4] + + +def getArch(): + os = platform.system().lower() + cpu_arch = platform.machine() + if cpu_arch == 'arm64': + return f'{os}-{cpu_arch}' + elif cpu_arch == 'x86_64': + return f'{os}-x64' + elif cpu_arch == 'AMD64': + return f'{os}-x64' + else: + log.error(f"Error: {os}-{cpu_arch} unsupported arch") + exit(1) + + +def runGitCommand(command): + result = subprocess.run(command, capture_output=True, text=True, shell=True) + if result.returncode != 0: + raise Exception(f"Git command failed: {result.stderr}") + return result.stdout.strip() + + +# 获取上传对象的进度 +def uploadCallback(transferredAmount, totalAmount, totalSeconds): + # 获取上传平均速率(KB/S) + speed = int(transferredAmount / totalSeconds / 1024) + # 获取上传进度百分比 + progress = int(transferredAmount * 100.0 / totalAmount) + print("\r", end="") + print("Speed: {} KB/S progress: {}%: ".format(speed, progress), "▓" * (progress // 2), end="") + sys.stdout.flush() + if progress == 100: + print("") diff --git a/attachment/scripts/zip_artifacts.py b/attachment/scripts/zip_artifacts.py new file mode 100644 index 0000000000000000000000000000000000000000..6c482efcf90d384f658365ec8958451d54cb877b --- /dev/null +++ b/attachment/scripts/zip_artifacts.py @@ -0,0 +1,106 @@ +import json +import os +import zipfile +import logging +from utils import getArch +from pathlib import PurePath +from pathlib import Path + +log = logging.getLogger(__name__) + +artifact_json = "artifacts_files.json" + + +def getFileMap(): + script_path = os.path.realpath(__file__) + script_dir = PurePath(script_path).parent + zip_artifact_json = script_dir.joinpath(artifact_json).__str__() + with open(zip_artifact_json, 'r') as jsonFile: + jsonData = json.load(jsonFile) + return jsonData + + +# BUILD.gn file does not exist in out/host_release/shader_lib. +# However, Google's flutter artifacts.zip contains it, so I add it manually here +# See. https://storage.flutter-io.cn/flutter_infra_release/flutter/edd8546116457bdf1c5bdfb13ecb9463d2bb5ed4/darwin-arm64/artifacts.zip + +shader_flutter_build = '''# Copyright 2013 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. + +copy("flutter") { + sources = [ "runtime_effect.glsl" ] + outputs = [ "$root_out_dir/shader_lib/flutter/{{source_file_part}}" ] +} +''' + +shader_impeller_build = '''# Copyright 2013 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. + +copy("impeller") { + sources = [ + "blending.glsl", + "branching.glsl", + "color.glsl", + "constants.glsl", + "conversions.glsl", + "dithering.glsl", + "external_texture_oes.glsl", + "gaussian.glsl", + "gradient.glsl", + "path.glsl", + "texture.glsl", + "tile_mode.glsl", + "transform.glsl", + "types.glsl", + ] + outputs = [ "$root_out_dir/shader_lib/impeller/{{source_file_part}}" ] +} +''' + +shader_gn_build = '''# Copyright 2013 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. + +group("shader_lib") { + deps = [ + "./flutter", + "./impeller", + ] +} +''' + + +def genZipFile(): + engine_project_root_dir = Path(os.path.realpath(__file__)).parents[4] + files_map = getFileMap()[getArch()] + with zipfile.ZipFile(engine_project_root_dir.joinpath('artifacts.zip'), 'w', + zipfile.ZIP_DEFLATED) as zipf: + for file_to_zip in files_map: + path = engine_project_root_dir.joinpath(file_to_zip) + if not path.exists(): + log.error(f"Error: file {path} does not exist") + exit(2) + else: + if path.is_file(): + log.info('Compressing:' + path.__str__()) + zipf.write(path, files_map[file_to_zip]) + elif Path(path).is_dir(): + dir_name_in_zip = files_map[file_to_zip] + for entry in path.rglob("*"): + log.info( + 'Compressing:' + Path(dir_name_in_zip).joinpath(entry.relative_to(path)).__str__() + ) + zipf.write(entry, Path(dir_name_in_zip).joinpath(entry.relative_to(path))) + zipf.writestr('shader_lib/flutter/BUILD.gn', shader_flutter_build) + zipf.writestr('shader_lib/impeller/BUILD.gn', shader_impeller_build) + zipf.writestr('shader_lib/BUILD.gn', shader_gn_build) + + +def main(): + genZipFile() + + +if __name__ == "__main__": + exit(main()) diff --git a/attachment/scripts/zip_dart_sdk.py b/attachment/scripts/zip_dart_sdk.py new file mode 100644 index 0000000000000000000000000000000000000000..a727ed9ff0e348033bf328c402700e8c92e5bfca --- /dev/null +++ b/attachment/scripts/zip_dart_sdk.py @@ -0,0 +1,31 @@ +import os +import zipfile +import platform +import logging +from pathlib import Path +from utils import getArch + +log = logging.getLogger(__name__) + + +def genZipFile(): + engine_project_root_dir = Path(os.path.realpath(__file__)).parents[4] + dart_sdk_path = engine_project_root_dir.joinpath("src/out/host_release/dart-sdk") + host_release_path = engine_project_root_dir.joinpath("src/out/host_release/") + arch = getArch() + with zipfile.ZipFile(engine_project_root_dir.joinpath(f'dart-sdk-{arch}.zip'), 'w', + zipfile.ZIP_DEFLATED) as zipf: + for entry in dart_sdk_path.rglob("*"): + if (entry.is_dir()): + continue + else: + print(entry.relative_to(host_release_path)) + zipf.write(entry, entry.relative_to(host_release_path)) + + +def main(): + genZipFile() + + +if __name__ == "__main__": + exit(main()) diff --git a/attachment/scripts/zip_flutter_patched_sdk.py b/attachment/scripts/zip_flutter_patched_sdk.py new file mode 100644 index 0000000000000000000000000000000000000000..85d312fce2f6e3380fd88c03ec127cfc69ad00e2 --- /dev/null +++ b/attachment/scripts/zip_flutter_patched_sdk.py @@ -0,0 +1,39 @@ +import os +import zipfile +import logging +from pathlib import Path + +log = logging.getLogger(__name__) + + +def genZipFile(is_product): + if is_product: + product_suffix = '_product' + sdk_rel_path = 'src/out/ohos_release_arm64/flutter_patched_sdk/' + zip_file_name = 'flutter_patched_sdk_product.zip' + else: + product_suffix = '' + sdk_rel_path = 'src/out/ohos_debug_unopt_arm64/flutter_patched_sdk/' + zip_file_name = 'flutter_patched_sdk.zip' + print(f'zipping {zip_file_name}...') + engine_project_root_path = Path(os.path.realpath(__file__)).parents[4] + patched_sdk_path = engine_project_root_path.joinpath(sdk_rel_path) + zip_file_prefix = Path('flutter_patched_sdk' + product_suffix) + with zipfile.ZipFile(engine_project_root_path.joinpath(zip_file_name), 'w', + zipfile.ZIP_DEFLATED) as zipf: + for entry in patched_sdk_path.rglob("*.dill"): + if (entry.is_dir()): + continue + else: + zip_path = zip_file_prefix.joinpath(entry.relative_to(patched_sdk_path)) + print(zip_path) + zipf.write(entry, zip_path) + + +def main(): + genZipFile(True) + genZipFile(False) + + +if __name__ == "__main__": + exit(main()) diff --git a/attachment/scripts/zip_sky_engine.py b/attachment/scripts/zip_sky_engine.py new file mode 100644 index 0000000000000000000000000000000000000000..d25a6b4ea3aed0651e20bb5866e7ba3f8238a570 --- /dev/null +++ b/attachment/scripts/zip_sky_engine.py @@ -0,0 +1,35 @@ +import os +import zipfile +import logging +from pathlib import Path +from utils import getArch + +log = logging.getLogger(__name__) + + +def genZipFile(): + engine_project_root_dir = Path(os.path.realpath(__file__)).parents[4] + sky_engine_path = engine_project_root_dir.joinpath("src/out/host_release/gen/dart-pkg/sky_engine") + host_release_path = engine_project_root_dir.joinpath("src/out/host_release/gen/dart-pkg") + arch = getArch() + with zipfile.ZipFile(engine_project_root_dir.joinpath('sky_engine.zip'), 'w', + zipfile.ZIP_DEFLATED) as zipf: + for entry in sky_engine_path.rglob("*"): + if (entry.is_dir()): + continue + elif (entry.is_symlink()): + link = str(entry) + real = str(entry.readlink()) + print(f'found sym_link: {link} -> {real}') + zipf.write(real, entry.relative_to(host_release_path)) + else: + print(entry.relative_to(host_release_path)) + zipf.write(entry, entry.relative_to(host_release_path)) + + +def main(): + genZipFile() + + +if __name__ == "__main__": + exit(main()) diff --git a/build/archives/BUILD.gn b/build/archives/BUILD.gn index fb7f8294add49185a4178e8d570bf2947974e455..8996fa93c1f9a2a0914ff072bf6e4dfb6e0cf9c8 100644 --- a/build/archives/BUILD.gn +++ b/build/archives/BUILD.gn @@ -21,7 +21,7 @@ generated_file("artifacts_entitlement_config") { } } -if (build_engine_artifacts && !is_android) { +if (build_engine_artifacts && !is_android && !is_ohos) { zip_bundle("artifacts") { deps = [ "$dart_src/runtime/bin:gen_snapshot", @@ -125,6 +125,12 @@ if (build_engine_artifacts && !is_android) { } } +if (is_ohos) { + group("artifacts") { + deps = [ "//flutter/shell/platform/ohos:flutter_har_zip" ] + } +} + if (host_os == "linux" || host_os == "win") { group("embedder") { deps = [ "//flutter/shell/platform/embedder:embedder-archive" ] diff --git a/build/dart/rules.gni b/build/dart/rules.gni index ec70bac7bc125a3908edef465f59e492c650720a..7ddce71ee5c4a10ea448ee03c7b327a24f6b171f 100644 --- a/build/dart/rules.gni +++ b/build/dart/rules.gni @@ -220,6 +220,18 @@ template("flutter_snapshot") { "--snapshot_kind=app-aot-elf", "--elf=" + rebase_path(libapp), ] + } else if (is_ohos) { + if (defined(invoker.output_aot_lib)) { + output_aot_lib = invoker.output_aot_lib + } else { + output_aot_lib = "libapp.so" + } + libapp = "$target_gen_dir/ohos/libs/$ohos_app_abi/$output_aot_lib" + outputs += [ libapp ] + args += [ + "--snapshot_kind=app-aot-elf", + "--elf=" + rebase_path(libapp), + ] } else { assert(false) } diff --git a/build/secondary/flutter/third_party/glfw/BUILD.gn b/build/secondary/flutter/third_party/glfw/BUILD.gn index cf6681051178a77172b3ec213576aa8c80e19da1..dfb13047224a0cb99a93dfeefbd85cfaf9267680 100644 --- a/build/secondary/flutter/third_party/glfw/BUILD.gn +++ b/build/secondary/flutter/third_party/glfw/BUILD.gn @@ -58,6 +58,34 @@ source_set("glfw") { libs = [ "Gdi32.lib" ] defines = [ "_GLFW_WIN32" ] + } else if (is_ohos) { + sources += [ + "$_checkout_dir/src/glx_context.c", + "$_checkout_dir/src/glx_context.h", + "$_checkout_dir/src/posix_time.c", + "$_checkout_dir/src/posix_time.h", + "$_checkout_dir/src/posix_thread.c", + "$_checkout_dir/src/posix_thread.h", + "$_checkout_dir/src/x11_init.c", + "$_checkout_dir/src/x11_monitor.c", + "$_checkout_dir/src/x11_platform.h", + "$_checkout_dir/src/x11_window.c", + "$_checkout_dir/src/xkb_unicode.c", + "$_checkout_dir/src/xkb_unicode.h", + ] + + defines = [ + "_GLFW_X11", + "_GLFW_HAS_XF86VM", + ] + + libs = [ + "X11", + "Xcursor", + "Xinerama", + "Xrandr", + "Xxf86vm", + ] } else if (is_linux) { sources += [ "$_checkout_dir/src/glx_context.c", diff --git a/ci/bin/format.dart b/ci/bin/format.dart index 49f019f7b2c918b781ba3b404b515096eeeb712b..84ebf55029ee6e1a903f9f07d6ad216dd444994d 100644 --- a/ci/bin/format.dart +++ b/ci/bin/format.dart @@ -1070,7 +1070,7 @@ Future _getDiffBaseRevision(ProcessManager processManager, Directory rep if (upstreamUrl.isEmpty) { upstream = 'origin'; } - await _runGit(['fetch', upstream, 'main'], processRunner); + await _runGit(['fetch', upstream, 'oh-3.22.0'], processRunner); String result = ''; try { // This is the preferred command to use, but developer checkouts often do diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 312f7fbc718774d3c7df9a719d9fff69f2ca5a22..320e0b794d26969b31c8d2dca9f4ac4ed944ba07 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,2449 +1,2449 @@ -Signature: be8ce23f4fb161952d9fa6424a6bf111 - -==================================================================================================== -LIBRARY: libcxx -LIBRARY: libcxxabi -ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/charconv.cpp -ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/common.h -ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/d2fixed.h -ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/d2fixed_full_table.h -ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/d2s.h -ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/d2s_full_table.h -ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/d2s_intrinsics.h -ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/digit_table.h -ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/f2s.h -ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/ryu.h -ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/to_chars_floating_point.h -ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/ryu/d2fixed.cpp -ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/ryu/d2s.cpp -ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/ryu/f2s.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/adjacent_find.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/all_of.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/any_of.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/binary_search.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/clamp.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/comp.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/comp_ref_type.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/copy.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/copy_backward.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/copy_if.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/copy_n.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/count.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/count_if.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/equal.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/equal_range.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/fill.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/fill_n.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/find.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/find_end.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/find_first_of.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/find_if.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/find_if_not.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/for_each.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/for_each_n.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/generate.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/generate_n.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/half_positive.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/in_found_result.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/in_fun_result.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/in_in_out_result.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/in_in_result.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/in_out_out_result.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/in_out_result.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/includes.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/inplace_merge.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/is_heap.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/is_heap_until.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/is_partitioned.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/is_permutation.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/is_sorted.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/is_sorted_until.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/iter_swap.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/iterator_operations.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/lexicographical_compare.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/lower_bound.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/make_heap.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/make_projected.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/max.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/max_element.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/merge.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/min.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/min_element.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/min_max_result.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/minmax.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/minmax_element.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/mismatch.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/move.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/move_backward.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/next_permutation.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/none_of.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/nth_element.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/partial_sort.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/partial_sort_copy.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/partition.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/partition_copy.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/partition_point.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/pop_heap.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/prev_permutation.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/push_heap.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_adjacent_find.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_all_of.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_any_of.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_binary_search.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_copy.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_copy_backward.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_copy_if.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_copy_n.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_count.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_count_if.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_equal.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_fill.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_fill_n.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_find.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_find_first_of.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_find_if.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_find_if_not.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_for_each.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_for_each_n.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_is_partitioned.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_is_sorted.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_is_sorted_until.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_lexicographical_compare.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_lower_bound.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_max.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_max_element.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_merge.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_min.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_min_element.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_minmax.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_minmax_element.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_mismatch.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_move.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_move_backward.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_none_of.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_nth_element.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_remove.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_remove_if.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_replace.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_replace_if.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_reverse.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_set_difference.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_sort.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_stable_sort.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_swap_ranges.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_transform.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_upper_bound.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/remove.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/remove_copy.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/remove_copy_if.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/remove_if.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/replace.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/replace_copy.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/replace_copy_if.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/replace_if.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/reverse.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/reverse_copy.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/rotate.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/rotate_copy.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/sample.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/search.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/search_n.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/set_difference.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/set_intersection.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/set_symmetric_difference.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/set_union.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/shift_left.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/shift_right.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/shuffle.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/sift_down.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/sort.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/sort_heap.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/stable_partition.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/stable_sort.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/swap_ranges.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/transform.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/unique.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/unique_copy.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/unwrap_iter.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/upper_bound.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__assert -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__availability -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__bit/bit_cast.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__bit/byteswap.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__bit_reference -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__bits -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__bsd_locale_defaults.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__bsd_locale_fallbacks.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__charconv/chars_format.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__charconv/from_chars_result.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__charconv/tables.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__charconv/to_chars_base_10.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__charconv/to_chars_result.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/calendar.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/convert_to_timespec.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/day.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/duration.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/file_clock.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/hh_mm_ss.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/high_resolution_clock.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/literals.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/month.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/month_weekday.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/monthday.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/steady_clock.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/system_clock.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/time_point.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/weekday.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/year.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/year_month.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/year_month_day.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/year_month_weekday.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/common_comparison_category.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/compare_partial_order_fallback.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/compare_strong_order_fallback.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/compare_three_way.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/compare_three_way_result.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/compare_weak_order_fallback.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/is_eq.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/ordering.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/partial_order.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/strong_order.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/synth_three_way.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/three_way_comparable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/weak_order.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/arithmetic.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/assignable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/boolean_testable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/class_or_enum.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/common_reference_with.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/common_with.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/constructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/convertible_to.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/copyable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/derived_from.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/destructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/different_from.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/equality_comparable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/invocable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/movable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/predicate.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/regular.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/relation.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/same_as.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/semiregular.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/swappable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/totally_ordered.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__config -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__config_site.in -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__coroutine/coroutine_handle.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__coroutine/coroutine_traits.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__coroutine/noop_coroutine_handle.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__coroutine/trivial_awaitables.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__debug -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__debug_utils/randomize_range.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__errc -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/copy_options.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/directory_entry.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/directory_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/directory_options.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/file_status.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/file_time_type.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/file_type.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/filesystem_error.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/operations.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/path.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/path_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/perm_options.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/perms.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/recursive_directory_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/space_info.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/u8path.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/buffer.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/concepts.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/enable_insertable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_arg.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_arg_store.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_args.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_context.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_error.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_fwd.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_parse_context.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_string.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_to_n_result.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_bool.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_char.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_floating_point.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_integer.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_integral.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_output.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_pointer.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_string.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/parser_std_format_spec.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/binary_function.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/binary_negate.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/bind.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/bind_back.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/bind_front.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/binder1st.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/binder2nd.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/boyer_moore_searcher.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/compose.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/default_searcher.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/function.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/hash.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/identity.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/invoke.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/is_transparent.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/mem_fn.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/mem_fun_ref.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/not_fn.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/operations.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/perfect_forward.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/pointer_to_binary_function.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/pointer_to_unary_function.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/ranges_operations.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/reference_wrapper.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/unary_function.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/unary_negate.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/unwrap_ref.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/weak_result_type.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__fwd/span.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__fwd/string_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__hash_table -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ios/fpos.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/access.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/advance.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/back_insert_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/bounded_iter.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/common_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/concepts.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/counted_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/data.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/default_sentinel.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/distance.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/empty.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/erase_if_container.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/front_insert_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/incrementable_traits.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/indirectly_comparable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/insert_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/istream_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/istreambuf_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/iter_move.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/iter_swap.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/iterator_traits.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/mergeable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/move_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/move_sentinel.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/next.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/ostream_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/ostreambuf_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/permutable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/prev.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/projected.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/readable_traits.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/reverse_access.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/reverse_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/size.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/sortable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/unreachable_sentinel.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/wrap_iter.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__locale -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__mbstate_t.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/addressof.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/allocate_at_least.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/allocation_guard.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/allocator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/allocator_arg_t.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/allocator_traits.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/assume_aligned.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/auto_ptr.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/compressed_pair.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/concepts.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/construct_at.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/pointer_traits.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/ranges_construct_at.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/ranges_uninitialized_algorithms.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/raw_storage_iterator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/shared_ptr.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/temporary_buffer.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/uninitialized_algorithms.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/unique_ptr.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/uses_allocator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/voidify.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__mutex_base -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__node_handle -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/accumulate.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/adjacent_difference.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/exclusive_scan.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/gcd_lcm.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/inclusive_scan.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/inner_product.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/iota.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/midpoint.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/partial_sum.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/reduce.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/transform_exclusive_scan.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/transform_inclusive_scan.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/transform_reduce.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/bernoulli_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/binomial_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/cauchy_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/chi_squared_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/clamp_to_integral.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/default_random_engine.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/discard_block_engine.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/discrete_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/exponential_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/extreme_value_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/fisher_f_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/gamma_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/generate_canonical.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/geometric_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/independent_bits_engine.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/is_seed_sequence.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/is_valid.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/knuth_b.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/linear_congruential_engine.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/log2.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/lognormal_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/mersenne_twister_engine.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/negative_binomial_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/normal_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/piecewise_constant_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/piecewise_linear_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/poisson_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/random_device.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/ranlux.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/seed_seq.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/shuffle_order_engine.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/student_t_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/subtract_with_carry_engine.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/uniform_int_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/uniform_random_bit_generator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/uniform_real_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/weibull_distribution.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/access.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/all.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/common_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/concepts.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/copyable_box.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/counted.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/dangling.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/data.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/drop_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/empty.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/empty_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/enable_borrowed_range.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/enable_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/filter_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/iota_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/join_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/lazy_split_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/non_propagating_cache.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/owning_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/range_adaptor.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/rbegin.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/ref_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/rend.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/reverse_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/single_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/size.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/subrange.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/take_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/transform_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/view_interface.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/views.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/zip_view.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__split_buffer -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__std_stream -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__string/char_traits.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__string/extern_template_lists.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/android/locale_bionic.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/fuchsia/xlocale.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/ibm/gettod_zos.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/ibm/limits.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/ibm/locale_mgmt_zos.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/ibm/nanosleep.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/ibm/support.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/ibm/xlocale.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/musl/xlocale.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/newlib/xlocale.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/openbsd/xlocale.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/solaris/floatingpoint.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/solaris/wchar.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/solaris/xlocale.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/win32/limits_msvc_win32.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/win32/locale_win32.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/xlocale/__nop_locale_mgmt.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/xlocale/__posix_l_fallback.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/xlocale/__strtonum_fallback.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__thread/poll_with_backoff.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__thread/timed_backoff_policy.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__threading_support -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__tree -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__tuple -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/add_const.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/add_cv.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/add_lvalue_reference.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/add_pointer.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/add_rvalue_reference.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/add_volatile.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/alignment_of.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/apply_cv.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/conditional.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/conjunction.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/decay.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/disjunction.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/enable_if.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/extent.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/has_unique_object_representation.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/has_virtual_destructor.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/integral_constant.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_abstract.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_aggregate.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_arithmetic.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_array.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_assignable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_base_of.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_bounded_array.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_callable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_class.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_compound.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_const.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_constant_evaluated.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_constructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_convertible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_copy_assignable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_copy_constructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_core_convertible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_default_constructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_destructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_empty.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_enum.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_final.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_floating_point.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_function.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_fundamental.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_integral.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_literal_type.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_member_function_pointer.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_member_object_pointer.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_member_pointer.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_move_assignable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_move_constructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_assignable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_constructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_copy_assignable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_copy_constructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_default_constructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_destructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_move_assignable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_move_constructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_null_pointer.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_object.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_pod.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_pointer.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_polymorphic.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_reference.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_reference_wrapper.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_referenceable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_same.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_scalar.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_scoped_enum.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_signed.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_standard_layout.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivial.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_assignable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_constructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_copy_assignable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_copy_constructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_copyable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_default_constructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_destructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_move_assignable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_move_constructible.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_unbounded_array.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_union.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_unsigned.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_void.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_volatile.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/negation.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/rank.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/remove_all_extents.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/remove_const.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/remove_cv.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/remove_extent.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/remove_pointer.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/remove_reference.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/remove_volatile.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/type_identity.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/underlying_type.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/void_t.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__undef_macros -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/as_const.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/auto_cast.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/cmp.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/declval.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/exchange.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/forward.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/in_place.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/integer_sequence.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/move.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/pair.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/piecewise_construct.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/priority_tag.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/rel_ops.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/swap.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/to_underlying.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/transaction.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/unreachable.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__variant/monostate.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/algorithm -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/any -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/array -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/atomic -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/barrier -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/bit -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/bitset -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cassert -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ccomplex -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cctype -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cerrno -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cfenv -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cfloat -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/charconv -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/chrono -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cinttypes -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ciso646 -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/climits -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/clocale -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cmath -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/codecvt -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/compare -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/complex -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/complex.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/concepts -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/condition_variable -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/coroutine -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/csetjmp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/csignal -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cstdarg -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cstdbool -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cstddef -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cstdint -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cstdio -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cstdlib -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cstring -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ctgmath -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ctime -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ctype.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cuchar -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cwchar -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cwctype -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/deque -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/errno.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/exception -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/execution -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/__config -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/__memory -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/algorithm -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/coroutine -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/deque -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/forward_list -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/functional -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/iterator -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/list -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/map -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/memory_resource -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/propagate_const -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/regex -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/set -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/simd -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/string -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/type_traits -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/unordered_map -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/unordered_set -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/utility -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/vector -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ext/__hash -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ext/hash_map -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ext/hash_set -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/fenv.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/filesystem -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/float.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/format -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/forward_list -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/fstream -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/functional -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/future -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/initializer_list -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/inttypes.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/iomanip -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ios -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/iosfwd -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/iostream -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/istream -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/iterator -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/latch -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/limits -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/limits.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/list -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/locale -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/locale.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/map -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/math.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/memory -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/mutex -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/new -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/numbers -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/numeric -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/optional -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ostream -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/queue -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/random -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ranges -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ratio -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/regex -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/scoped_allocator -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/semaphore -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/set -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/setjmp.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/shared_mutex -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/span -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/sstream -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stack -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stdatomic.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stdbool.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stddef.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stdexcept -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stdint.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stdio.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stdlib.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/streambuf -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/string -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/string.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/string_view -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/strstream -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/system_error -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/tgmath.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/thread -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/tuple -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/type_traits -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/typeindex -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/typeinfo -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/uchar.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/unordered_map -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/unordered_set -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/utility -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/valarray -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/variant -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/vector -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/wchar.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/wctype.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/algorithm.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/any.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/assert.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/atomic.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/barrier.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/bind.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/charconv.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/chrono.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/condition_variable.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/condition_variable_destructor.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/debug.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/exception.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/experimental/memory_resource.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/filesystem/directory_iterator.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/filesystem/filesystem_common.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/filesystem/int128_builtins.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/filesystem/operations.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/filesystem/posix_compat.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/format.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/functional.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/future.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/hash.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/apple_availability.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/atomic_support.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/config_elast.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/refstring.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/common.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/d2fixed.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/d2fixed_full_table.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/d2s.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/d2s_full_table.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/d2s_intrinsics.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/digit_table.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/f2s.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/ryu.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/sso_allocator.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/to_chars_floating_point.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/ios.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/ios.instantiations.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/iostream.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/legacy_debug_handler.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/legacy_pointer_safety.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/locale.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/memory.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/mutex.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/mutex_destructor.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/new.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/optional.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/random.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/random_shuffle.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/regex.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/ryu/d2fixed.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/ryu/d2s.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/ryu/f2s.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/shared_mutex.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/stdexcept.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/string.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/strstream.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/ibm/mbsnrtowcs.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/ibm/wcsnrtombs.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/ibm/xlocale_zos.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_fallback.ipp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_glibcxx.ipp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_libcxxabi.ipp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_libcxxrt.ipp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_msvc.ipp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_pointer_cxxabi.ipp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_pointer_glibcxx.ipp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_pointer_msvc.ipp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_pointer_unimplemented.ipp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/new_handler_fallback.ipp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/stdexcept_default.ipp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/stdexcept_vcruntime.ipp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/win32/locale_win32.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/win32/support.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/win32/thread_win32.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/system_error.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/thread.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/typeinfo.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/utility.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/valarray.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/variant.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/vector.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/include/__cxxabi_config.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/include/cxxabi.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/abort_message.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/abort_message.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/aix_state_tab_eh.inc -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_aux_runtime.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_default_handlers.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_demangle.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_exception.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_exception.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_exception_storage.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_guard.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_guard_impl.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_handlers.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_handlers.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_noexception.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_personality.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_thread_atexit.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_vector.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_virtual.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/demangle/DemangleConfig.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/demangle/ItaniumDemangle.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/demangle/ItaniumNodes.def -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/demangle/StringView.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/demangle/Utility.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/fallback_malloc.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/fallback_malloc.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/private_typeinfo.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/private_typeinfo.h -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/stdlib_exception.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/stdlib_new_delete.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/stdlib_stdexcept.cpp -ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/stdlib_typeinfo.cpp -TYPE: LicenseType.llvm -FILE: ../../../third_party/libcxx/include/__algorithm/adjacent_find.h -FILE: ../../../third_party/libcxx/include/__algorithm/all_of.h -FILE: ../../../third_party/libcxx/include/__algorithm/any_of.h -FILE: ../../../third_party/libcxx/include/__algorithm/binary_search.h -FILE: ../../../third_party/libcxx/include/__algorithm/clamp.h -FILE: ../../../third_party/libcxx/include/__algorithm/comp.h -FILE: ../../../third_party/libcxx/include/__algorithm/comp_ref_type.h -FILE: ../../../third_party/libcxx/include/__algorithm/copy.h -FILE: ../../../third_party/libcxx/include/__algorithm/copy_backward.h -FILE: ../../../third_party/libcxx/include/__algorithm/copy_if.h -FILE: ../../../third_party/libcxx/include/__algorithm/copy_n.h -FILE: ../../../third_party/libcxx/include/__algorithm/count.h -FILE: ../../../third_party/libcxx/include/__algorithm/count_if.h -FILE: ../../../third_party/libcxx/include/__algorithm/equal.h -FILE: ../../../third_party/libcxx/include/__algorithm/equal_range.h -FILE: ../../../third_party/libcxx/include/__algorithm/fill.h -FILE: ../../../third_party/libcxx/include/__algorithm/fill_n.h -FILE: ../../../third_party/libcxx/include/__algorithm/find.h -FILE: ../../../third_party/libcxx/include/__algorithm/find_end.h -FILE: ../../../third_party/libcxx/include/__algorithm/find_first_of.h -FILE: ../../../third_party/libcxx/include/__algorithm/find_if.h -FILE: ../../../third_party/libcxx/include/__algorithm/find_if_not.h -FILE: ../../../third_party/libcxx/include/__algorithm/for_each.h -FILE: ../../../third_party/libcxx/include/__algorithm/for_each_n.h -FILE: ../../../third_party/libcxx/include/__algorithm/generate.h -FILE: ../../../third_party/libcxx/include/__algorithm/generate_n.h -FILE: ../../../third_party/libcxx/include/__algorithm/half_positive.h -FILE: ../../../third_party/libcxx/include/__algorithm/in_found_result.h -FILE: ../../../third_party/libcxx/include/__algorithm/in_fun_result.h -FILE: ../../../third_party/libcxx/include/__algorithm/in_in_out_result.h -FILE: ../../../third_party/libcxx/include/__algorithm/in_in_result.h -FILE: ../../../third_party/libcxx/include/__algorithm/in_out_out_result.h -FILE: ../../../third_party/libcxx/include/__algorithm/in_out_result.h -FILE: ../../../third_party/libcxx/include/__algorithm/includes.h -FILE: ../../../third_party/libcxx/include/__algorithm/inplace_merge.h -FILE: ../../../third_party/libcxx/include/__algorithm/is_heap.h -FILE: ../../../third_party/libcxx/include/__algorithm/is_heap_until.h -FILE: ../../../third_party/libcxx/include/__algorithm/is_partitioned.h -FILE: ../../../third_party/libcxx/include/__algorithm/is_permutation.h -FILE: ../../../third_party/libcxx/include/__algorithm/is_sorted.h -FILE: ../../../third_party/libcxx/include/__algorithm/is_sorted_until.h -FILE: ../../../third_party/libcxx/include/__algorithm/iter_swap.h -FILE: ../../../third_party/libcxx/include/__algorithm/iterator_operations.h -FILE: ../../../third_party/libcxx/include/__algorithm/lexicographical_compare.h -FILE: ../../../third_party/libcxx/include/__algorithm/lower_bound.h -FILE: ../../../third_party/libcxx/include/__algorithm/make_heap.h -FILE: ../../../third_party/libcxx/include/__algorithm/make_projected.h -FILE: ../../../third_party/libcxx/include/__algorithm/max.h -FILE: ../../../third_party/libcxx/include/__algorithm/max_element.h -FILE: ../../../third_party/libcxx/include/__algorithm/merge.h -FILE: ../../../third_party/libcxx/include/__algorithm/min.h -FILE: ../../../third_party/libcxx/include/__algorithm/min_element.h -FILE: ../../../third_party/libcxx/include/__algorithm/min_max_result.h -FILE: ../../../third_party/libcxx/include/__algorithm/minmax.h -FILE: ../../../third_party/libcxx/include/__algorithm/minmax_element.h -FILE: ../../../third_party/libcxx/include/__algorithm/mismatch.h -FILE: ../../../third_party/libcxx/include/__algorithm/move.h -FILE: ../../../third_party/libcxx/include/__algorithm/move_backward.h -FILE: ../../../third_party/libcxx/include/__algorithm/next_permutation.h -FILE: ../../../third_party/libcxx/include/__algorithm/none_of.h -FILE: ../../../third_party/libcxx/include/__algorithm/nth_element.h -FILE: ../../../third_party/libcxx/include/__algorithm/partial_sort.h -FILE: ../../../third_party/libcxx/include/__algorithm/partial_sort_copy.h -FILE: ../../../third_party/libcxx/include/__algorithm/partition.h -FILE: ../../../third_party/libcxx/include/__algorithm/partition_copy.h -FILE: ../../../third_party/libcxx/include/__algorithm/partition_point.h -FILE: ../../../third_party/libcxx/include/__algorithm/pop_heap.h -FILE: ../../../third_party/libcxx/include/__algorithm/prev_permutation.h -FILE: ../../../third_party/libcxx/include/__algorithm/push_heap.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_adjacent_find.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_all_of.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_any_of.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_binary_search.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_copy.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_copy_backward.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_copy_if.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_copy_n.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_count.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_count_if.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_equal.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_fill.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_fill_n.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_find.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_find_first_of.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_find_if.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_find_if_not.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_for_each.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_for_each_n.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_is_partitioned.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_is_sorted.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_is_sorted_until.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_lexicographical_compare.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_lower_bound.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_max.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_max_element.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_merge.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_min.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_min_element.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_minmax.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_minmax_element.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_mismatch.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_move.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_move_backward.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_none_of.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_nth_element.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_remove.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_remove_if.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_replace.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_replace_if.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_reverse.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_set_difference.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_sort.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_stable_sort.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_swap_ranges.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_transform.h -FILE: ../../../third_party/libcxx/include/__algorithm/ranges_upper_bound.h -FILE: ../../../third_party/libcxx/include/__algorithm/remove.h -FILE: ../../../third_party/libcxx/include/__algorithm/remove_copy.h -FILE: ../../../third_party/libcxx/include/__algorithm/remove_copy_if.h -FILE: ../../../third_party/libcxx/include/__algorithm/remove_if.h -FILE: ../../../third_party/libcxx/include/__algorithm/replace.h -FILE: ../../../third_party/libcxx/include/__algorithm/replace_copy.h -FILE: ../../../third_party/libcxx/include/__algorithm/replace_copy_if.h -FILE: ../../../third_party/libcxx/include/__algorithm/replace_if.h -FILE: ../../../third_party/libcxx/include/__algorithm/reverse.h -FILE: ../../../third_party/libcxx/include/__algorithm/reverse_copy.h -FILE: ../../../third_party/libcxx/include/__algorithm/rotate.h -FILE: ../../../third_party/libcxx/include/__algorithm/rotate_copy.h -FILE: ../../../third_party/libcxx/include/__algorithm/sample.h -FILE: ../../../third_party/libcxx/include/__algorithm/search.h -FILE: ../../../third_party/libcxx/include/__algorithm/search_n.h -FILE: ../../../third_party/libcxx/include/__algorithm/set_difference.h -FILE: ../../../third_party/libcxx/include/__algorithm/set_intersection.h -FILE: ../../../third_party/libcxx/include/__algorithm/set_symmetric_difference.h -FILE: ../../../third_party/libcxx/include/__algorithm/set_union.h -FILE: ../../../third_party/libcxx/include/__algorithm/shift_left.h -FILE: ../../../third_party/libcxx/include/__algorithm/shift_right.h -FILE: ../../../third_party/libcxx/include/__algorithm/shuffle.h -FILE: ../../../third_party/libcxx/include/__algorithm/sift_down.h -FILE: ../../../third_party/libcxx/include/__algorithm/sort.h -FILE: ../../../third_party/libcxx/include/__algorithm/sort_heap.h -FILE: ../../../third_party/libcxx/include/__algorithm/stable_partition.h -FILE: ../../../third_party/libcxx/include/__algorithm/stable_sort.h -FILE: ../../../third_party/libcxx/include/__algorithm/swap_ranges.h -FILE: ../../../third_party/libcxx/include/__algorithm/transform.h -FILE: ../../../third_party/libcxx/include/__algorithm/unique.h -FILE: ../../../third_party/libcxx/include/__algorithm/unique_copy.h -FILE: ../../../third_party/libcxx/include/__algorithm/unwrap_iter.h -FILE: ../../../third_party/libcxx/include/__algorithm/upper_bound.h -FILE: ../../../third_party/libcxx/include/__assert -FILE: ../../../third_party/libcxx/include/__availability -FILE: ../../../third_party/libcxx/include/__bit/bit_cast.h -FILE: ../../../third_party/libcxx/include/__bit/byteswap.h -FILE: ../../../third_party/libcxx/include/__bit_reference -FILE: ../../../third_party/libcxx/include/__bits -FILE: ../../../third_party/libcxx/include/__bsd_locale_defaults.h -FILE: ../../../third_party/libcxx/include/__bsd_locale_fallbacks.h -FILE: ../../../third_party/libcxx/include/__charconv/chars_format.h -FILE: ../../../third_party/libcxx/include/__charconv/from_chars_result.h -FILE: ../../../third_party/libcxx/include/__charconv/tables.h -FILE: ../../../third_party/libcxx/include/__charconv/to_chars_base_10.h -FILE: ../../../third_party/libcxx/include/__charconv/to_chars_result.h -FILE: ../../../third_party/libcxx/include/__chrono/calendar.h -FILE: ../../../third_party/libcxx/include/__chrono/convert_to_timespec.h -FILE: ../../../third_party/libcxx/include/__chrono/day.h -FILE: ../../../third_party/libcxx/include/__chrono/duration.h -FILE: ../../../third_party/libcxx/include/__chrono/file_clock.h -FILE: ../../../third_party/libcxx/include/__chrono/hh_mm_ss.h -FILE: ../../../third_party/libcxx/include/__chrono/high_resolution_clock.h -FILE: ../../../third_party/libcxx/include/__chrono/literals.h -FILE: ../../../third_party/libcxx/include/__chrono/month.h -FILE: ../../../third_party/libcxx/include/__chrono/month_weekday.h -FILE: ../../../third_party/libcxx/include/__chrono/monthday.h -FILE: ../../../third_party/libcxx/include/__chrono/steady_clock.h -FILE: ../../../third_party/libcxx/include/__chrono/system_clock.h -FILE: ../../../third_party/libcxx/include/__chrono/time_point.h -FILE: ../../../third_party/libcxx/include/__chrono/weekday.h -FILE: ../../../third_party/libcxx/include/__chrono/year.h -FILE: ../../../third_party/libcxx/include/__chrono/year_month.h -FILE: ../../../third_party/libcxx/include/__chrono/year_month_day.h -FILE: ../../../third_party/libcxx/include/__chrono/year_month_weekday.h -FILE: ../../../third_party/libcxx/include/__compare/common_comparison_category.h -FILE: ../../../third_party/libcxx/include/__compare/compare_partial_order_fallback.h -FILE: ../../../third_party/libcxx/include/__compare/compare_strong_order_fallback.h -FILE: ../../../third_party/libcxx/include/__compare/compare_three_way.h -FILE: ../../../third_party/libcxx/include/__compare/compare_three_way_result.h -FILE: ../../../third_party/libcxx/include/__compare/compare_weak_order_fallback.h -FILE: ../../../third_party/libcxx/include/__compare/is_eq.h -FILE: ../../../third_party/libcxx/include/__compare/ordering.h -FILE: ../../../third_party/libcxx/include/__compare/partial_order.h -FILE: ../../../third_party/libcxx/include/__compare/strong_order.h -FILE: ../../../third_party/libcxx/include/__compare/synth_three_way.h -FILE: ../../../third_party/libcxx/include/__compare/three_way_comparable.h -FILE: ../../../third_party/libcxx/include/__compare/weak_order.h -FILE: ../../../third_party/libcxx/include/__concepts/arithmetic.h -FILE: ../../../third_party/libcxx/include/__concepts/assignable.h -FILE: ../../../third_party/libcxx/include/__concepts/boolean_testable.h -FILE: ../../../third_party/libcxx/include/__concepts/class_or_enum.h -FILE: ../../../third_party/libcxx/include/__concepts/common_reference_with.h -FILE: ../../../third_party/libcxx/include/__concepts/common_with.h -FILE: ../../../third_party/libcxx/include/__concepts/constructible.h -FILE: ../../../third_party/libcxx/include/__concepts/convertible_to.h -FILE: ../../../third_party/libcxx/include/__concepts/copyable.h -FILE: ../../../third_party/libcxx/include/__concepts/derived_from.h -FILE: ../../../third_party/libcxx/include/__concepts/destructible.h -FILE: ../../../third_party/libcxx/include/__concepts/different_from.h -FILE: ../../../third_party/libcxx/include/__concepts/equality_comparable.h -FILE: ../../../third_party/libcxx/include/__concepts/invocable.h -FILE: ../../../third_party/libcxx/include/__concepts/movable.h -FILE: ../../../third_party/libcxx/include/__concepts/predicate.h -FILE: ../../../third_party/libcxx/include/__concepts/regular.h -FILE: ../../../third_party/libcxx/include/__concepts/relation.h -FILE: ../../../third_party/libcxx/include/__concepts/same_as.h -FILE: ../../../third_party/libcxx/include/__concepts/semiregular.h -FILE: ../../../third_party/libcxx/include/__concepts/swappable.h -FILE: ../../../third_party/libcxx/include/__concepts/totally_ordered.h -FILE: ../../../third_party/libcxx/include/__config -FILE: ../../../third_party/libcxx/include/__config_site.in -FILE: ../../../third_party/libcxx/include/__coroutine/coroutine_handle.h -FILE: ../../../third_party/libcxx/include/__coroutine/coroutine_traits.h -FILE: ../../../third_party/libcxx/include/__coroutine/noop_coroutine_handle.h -FILE: ../../../third_party/libcxx/include/__coroutine/trivial_awaitables.h -FILE: ../../../third_party/libcxx/include/__debug -FILE: ../../../third_party/libcxx/include/__debug_utils/randomize_range.h -FILE: ../../../third_party/libcxx/include/__errc -FILE: ../../../third_party/libcxx/include/__filesystem/copy_options.h -FILE: ../../../third_party/libcxx/include/__filesystem/directory_entry.h -FILE: ../../../third_party/libcxx/include/__filesystem/directory_iterator.h -FILE: ../../../third_party/libcxx/include/__filesystem/directory_options.h -FILE: ../../../third_party/libcxx/include/__filesystem/file_status.h -FILE: ../../../third_party/libcxx/include/__filesystem/file_time_type.h -FILE: ../../../third_party/libcxx/include/__filesystem/file_type.h -FILE: ../../../third_party/libcxx/include/__filesystem/filesystem_error.h -FILE: ../../../third_party/libcxx/include/__filesystem/operations.h -FILE: ../../../third_party/libcxx/include/__filesystem/path.h -FILE: ../../../third_party/libcxx/include/__filesystem/path_iterator.h -FILE: ../../../third_party/libcxx/include/__filesystem/perm_options.h -FILE: ../../../third_party/libcxx/include/__filesystem/perms.h -FILE: ../../../third_party/libcxx/include/__filesystem/recursive_directory_iterator.h -FILE: ../../../third_party/libcxx/include/__filesystem/space_info.h -FILE: ../../../third_party/libcxx/include/__filesystem/u8path.h -FILE: ../../../third_party/libcxx/include/__format/buffer.h -FILE: ../../../third_party/libcxx/include/__format/concepts.h -FILE: ../../../third_party/libcxx/include/__format/enable_insertable.h -FILE: ../../../third_party/libcxx/include/__format/format_arg.h -FILE: ../../../third_party/libcxx/include/__format/format_arg_store.h -FILE: ../../../third_party/libcxx/include/__format/format_args.h -FILE: ../../../third_party/libcxx/include/__format/format_context.h -FILE: ../../../third_party/libcxx/include/__format/format_error.h -FILE: ../../../third_party/libcxx/include/__format/format_fwd.h -FILE: ../../../third_party/libcxx/include/__format/format_parse_context.h -FILE: ../../../third_party/libcxx/include/__format/format_string.h -FILE: ../../../third_party/libcxx/include/__format/format_to_n_result.h -FILE: ../../../third_party/libcxx/include/__format/formatter.h -FILE: ../../../third_party/libcxx/include/__format/formatter_bool.h -FILE: ../../../third_party/libcxx/include/__format/formatter_char.h -FILE: ../../../third_party/libcxx/include/__format/formatter_floating_point.h -FILE: ../../../third_party/libcxx/include/__format/formatter_integer.h -FILE: ../../../third_party/libcxx/include/__format/formatter_integral.h -FILE: ../../../third_party/libcxx/include/__format/formatter_output.h -FILE: ../../../third_party/libcxx/include/__format/formatter_pointer.h -FILE: ../../../third_party/libcxx/include/__format/formatter_string.h -FILE: ../../../third_party/libcxx/include/__format/parser_std_format_spec.h -FILE: ../../../third_party/libcxx/include/__functional/binary_function.h -FILE: ../../../third_party/libcxx/include/__functional/binary_negate.h -FILE: ../../../third_party/libcxx/include/__functional/bind.h -FILE: ../../../third_party/libcxx/include/__functional/bind_back.h -FILE: ../../../third_party/libcxx/include/__functional/bind_front.h -FILE: ../../../third_party/libcxx/include/__functional/binder1st.h -FILE: ../../../third_party/libcxx/include/__functional/binder2nd.h -FILE: ../../../third_party/libcxx/include/__functional/boyer_moore_searcher.h -FILE: ../../../third_party/libcxx/include/__functional/compose.h -FILE: ../../../third_party/libcxx/include/__functional/default_searcher.h -FILE: ../../../third_party/libcxx/include/__functional/function.h -FILE: ../../../third_party/libcxx/include/__functional/hash.h -FILE: ../../../third_party/libcxx/include/__functional/identity.h -FILE: ../../../third_party/libcxx/include/__functional/invoke.h -FILE: ../../../third_party/libcxx/include/__functional/is_transparent.h -FILE: ../../../third_party/libcxx/include/__functional/mem_fn.h -FILE: ../../../third_party/libcxx/include/__functional/mem_fun_ref.h -FILE: ../../../third_party/libcxx/include/__functional/not_fn.h -FILE: ../../../third_party/libcxx/include/__functional/operations.h -FILE: ../../../third_party/libcxx/include/__functional/perfect_forward.h -FILE: ../../../third_party/libcxx/include/__functional/pointer_to_binary_function.h -FILE: ../../../third_party/libcxx/include/__functional/pointer_to_unary_function.h -FILE: ../../../third_party/libcxx/include/__functional/ranges_operations.h -FILE: ../../../third_party/libcxx/include/__functional/reference_wrapper.h -FILE: ../../../third_party/libcxx/include/__functional/unary_function.h -FILE: ../../../third_party/libcxx/include/__functional/unary_negate.h -FILE: ../../../third_party/libcxx/include/__functional/unwrap_ref.h -FILE: ../../../third_party/libcxx/include/__functional/weak_result_type.h -FILE: ../../../third_party/libcxx/include/__fwd/span.h -FILE: ../../../third_party/libcxx/include/__fwd/string_view.h -FILE: ../../../third_party/libcxx/include/__hash_table -FILE: ../../../third_party/libcxx/include/__ios/fpos.h -FILE: ../../../third_party/libcxx/include/__iterator/access.h -FILE: ../../../third_party/libcxx/include/__iterator/advance.h -FILE: ../../../third_party/libcxx/include/__iterator/back_insert_iterator.h -FILE: ../../../third_party/libcxx/include/__iterator/bounded_iter.h -FILE: ../../../third_party/libcxx/include/__iterator/common_iterator.h -FILE: ../../../third_party/libcxx/include/__iterator/concepts.h -FILE: ../../../third_party/libcxx/include/__iterator/counted_iterator.h -FILE: ../../../third_party/libcxx/include/__iterator/data.h -FILE: ../../../third_party/libcxx/include/__iterator/default_sentinel.h -FILE: ../../../third_party/libcxx/include/__iterator/distance.h -FILE: ../../../third_party/libcxx/include/__iterator/empty.h -FILE: ../../../third_party/libcxx/include/__iterator/erase_if_container.h -FILE: ../../../third_party/libcxx/include/__iterator/front_insert_iterator.h -FILE: ../../../third_party/libcxx/include/__iterator/incrementable_traits.h -FILE: ../../../third_party/libcxx/include/__iterator/indirectly_comparable.h -FILE: ../../../third_party/libcxx/include/__iterator/insert_iterator.h -FILE: ../../../third_party/libcxx/include/__iterator/istream_iterator.h -FILE: ../../../third_party/libcxx/include/__iterator/istreambuf_iterator.h -FILE: ../../../third_party/libcxx/include/__iterator/iter_move.h -FILE: ../../../third_party/libcxx/include/__iterator/iter_swap.h -FILE: ../../../third_party/libcxx/include/__iterator/iterator.h -FILE: ../../../third_party/libcxx/include/__iterator/iterator_traits.h -FILE: ../../../third_party/libcxx/include/__iterator/mergeable.h -FILE: ../../../third_party/libcxx/include/__iterator/move_iterator.h -FILE: ../../../third_party/libcxx/include/__iterator/move_sentinel.h -FILE: ../../../third_party/libcxx/include/__iterator/next.h -FILE: ../../../third_party/libcxx/include/__iterator/ostream_iterator.h -FILE: ../../../third_party/libcxx/include/__iterator/ostreambuf_iterator.h -FILE: ../../../third_party/libcxx/include/__iterator/permutable.h -FILE: ../../../third_party/libcxx/include/__iterator/prev.h -FILE: ../../../third_party/libcxx/include/__iterator/projected.h -FILE: ../../../third_party/libcxx/include/__iterator/readable_traits.h -FILE: ../../../third_party/libcxx/include/__iterator/reverse_access.h -FILE: ../../../third_party/libcxx/include/__iterator/reverse_iterator.h -FILE: ../../../third_party/libcxx/include/__iterator/size.h -FILE: ../../../third_party/libcxx/include/__iterator/sortable.h -FILE: ../../../third_party/libcxx/include/__iterator/unreachable_sentinel.h -FILE: ../../../third_party/libcxx/include/__iterator/wrap_iter.h -FILE: ../../../third_party/libcxx/include/__locale -FILE: ../../../third_party/libcxx/include/__mbstate_t.h -FILE: ../../../third_party/libcxx/include/__memory/addressof.h -FILE: ../../../third_party/libcxx/include/__memory/allocate_at_least.h -FILE: ../../../third_party/libcxx/include/__memory/allocation_guard.h -FILE: ../../../third_party/libcxx/include/__memory/allocator.h -FILE: ../../../third_party/libcxx/include/__memory/allocator_arg_t.h -FILE: ../../../third_party/libcxx/include/__memory/allocator_traits.h -FILE: ../../../third_party/libcxx/include/__memory/assume_aligned.h -FILE: ../../../third_party/libcxx/include/__memory/auto_ptr.h -FILE: ../../../third_party/libcxx/include/__memory/compressed_pair.h -FILE: ../../../third_party/libcxx/include/__memory/concepts.h -FILE: ../../../third_party/libcxx/include/__memory/construct_at.h -FILE: ../../../third_party/libcxx/include/__memory/pointer_traits.h -FILE: ../../../third_party/libcxx/include/__memory/ranges_construct_at.h -FILE: ../../../third_party/libcxx/include/__memory/ranges_uninitialized_algorithms.h -FILE: ../../../third_party/libcxx/include/__memory/raw_storage_iterator.h -FILE: ../../../third_party/libcxx/include/__memory/shared_ptr.h -FILE: ../../../third_party/libcxx/include/__memory/temporary_buffer.h -FILE: ../../../third_party/libcxx/include/__memory/uninitialized_algorithms.h -FILE: ../../../third_party/libcxx/include/__memory/unique_ptr.h -FILE: ../../../third_party/libcxx/include/__memory/uses_allocator.h -FILE: ../../../third_party/libcxx/include/__memory/voidify.h -FILE: ../../../third_party/libcxx/include/__mutex_base -FILE: ../../../third_party/libcxx/include/__node_handle -FILE: ../../../third_party/libcxx/include/__numeric/accumulate.h -FILE: ../../../third_party/libcxx/include/__numeric/adjacent_difference.h -FILE: ../../../third_party/libcxx/include/__numeric/exclusive_scan.h -FILE: ../../../third_party/libcxx/include/__numeric/gcd_lcm.h -FILE: ../../../third_party/libcxx/include/__numeric/inclusive_scan.h -FILE: ../../../third_party/libcxx/include/__numeric/inner_product.h -FILE: ../../../third_party/libcxx/include/__numeric/iota.h -FILE: ../../../third_party/libcxx/include/__numeric/midpoint.h -FILE: ../../../third_party/libcxx/include/__numeric/partial_sum.h -FILE: ../../../third_party/libcxx/include/__numeric/reduce.h -FILE: ../../../third_party/libcxx/include/__numeric/transform_exclusive_scan.h -FILE: ../../../third_party/libcxx/include/__numeric/transform_inclusive_scan.h -FILE: ../../../third_party/libcxx/include/__numeric/transform_reduce.h -FILE: ../../../third_party/libcxx/include/__random/bernoulli_distribution.h -FILE: ../../../third_party/libcxx/include/__random/binomial_distribution.h -FILE: ../../../third_party/libcxx/include/__random/cauchy_distribution.h -FILE: ../../../third_party/libcxx/include/__random/chi_squared_distribution.h -FILE: ../../../third_party/libcxx/include/__random/clamp_to_integral.h -FILE: ../../../third_party/libcxx/include/__random/default_random_engine.h -FILE: ../../../third_party/libcxx/include/__random/discard_block_engine.h -FILE: ../../../third_party/libcxx/include/__random/discrete_distribution.h -FILE: ../../../third_party/libcxx/include/__random/exponential_distribution.h -FILE: ../../../third_party/libcxx/include/__random/extreme_value_distribution.h -FILE: ../../../third_party/libcxx/include/__random/fisher_f_distribution.h -FILE: ../../../third_party/libcxx/include/__random/gamma_distribution.h -FILE: ../../../third_party/libcxx/include/__random/generate_canonical.h -FILE: ../../../third_party/libcxx/include/__random/geometric_distribution.h -FILE: ../../../third_party/libcxx/include/__random/independent_bits_engine.h -FILE: ../../../third_party/libcxx/include/__random/is_seed_sequence.h -FILE: ../../../third_party/libcxx/include/__random/is_valid.h -FILE: ../../../third_party/libcxx/include/__random/knuth_b.h -FILE: ../../../third_party/libcxx/include/__random/linear_congruential_engine.h -FILE: ../../../third_party/libcxx/include/__random/log2.h -FILE: ../../../third_party/libcxx/include/__random/lognormal_distribution.h -FILE: ../../../third_party/libcxx/include/__random/mersenne_twister_engine.h -FILE: ../../../third_party/libcxx/include/__random/negative_binomial_distribution.h -FILE: ../../../third_party/libcxx/include/__random/normal_distribution.h -FILE: ../../../third_party/libcxx/include/__random/piecewise_constant_distribution.h -FILE: ../../../third_party/libcxx/include/__random/piecewise_linear_distribution.h -FILE: ../../../third_party/libcxx/include/__random/poisson_distribution.h -FILE: ../../../third_party/libcxx/include/__random/random_device.h -FILE: ../../../third_party/libcxx/include/__random/ranlux.h -FILE: ../../../third_party/libcxx/include/__random/seed_seq.h -FILE: ../../../third_party/libcxx/include/__random/shuffle_order_engine.h -FILE: ../../../third_party/libcxx/include/__random/student_t_distribution.h -FILE: ../../../third_party/libcxx/include/__random/subtract_with_carry_engine.h -FILE: ../../../third_party/libcxx/include/__random/uniform_int_distribution.h -FILE: ../../../third_party/libcxx/include/__random/uniform_random_bit_generator.h -FILE: ../../../third_party/libcxx/include/__random/uniform_real_distribution.h -FILE: ../../../third_party/libcxx/include/__random/weibull_distribution.h -FILE: ../../../third_party/libcxx/include/__ranges/access.h -FILE: ../../../third_party/libcxx/include/__ranges/all.h -FILE: ../../../third_party/libcxx/include/__ranges/common_view.h -FILE: ../../../third_party/libcxx/include/__ranges/concepts.h -FILE: ../../../third_party/libcxx/include/__ranges/copyable_box.h -FILE: ../../../third_party/libcxx/include/__ranges/counted.h -FILE: ../../../third_party/libcxx/include/__ranges/dangling.h -FILE: ../../../third_party/libcxx/include/__ranges/data.h -FILE: ../../../third_party/libcxx/include/__ranges/drop_view.h -FILE: ../../../third_party/libcxx/include/__ranges/empty.h -FILE: ../../../third_party/libcxx/include/__ranges/empty_view.h -FILE: ../../../third_party/libcxx/include/__ranges/enable_borrowed_range.h -FILE: ../../../third_party/libcxx/include/__ranges/enable_view.h -FILE: ../../../third_party/libcxx/include/__ranges/filter_view.h -FILE: ../../../third_party/libcxx/include/__ranges/iota_view.h -FILE: ../../../third_party/libcxx/include/__ranges/join_view.h -FILE: ../../../third_party/libcxx/include/__ranges/lazy_split_view.h -FILE: ../../../third_party/libcxx/include/__ranges/non_propagating_cache.h -FILE: ../../../third_party/libcxx/include/__ranges/owning_view.h -FILE: ../../../third_party/libcxx/include/__ranges/range_adaptor.h -FILE: ../../../third_party/libcxx/include/__ranges/rbegin.h -FILE: ../../../third_party/libcxx/include/__ranges/ref_view.h -FILE: ../../../third_party/libcxx/include/__ranges/rend.h -FILE: ../../../third_party/libcxx/include/__ranges/reverse_view.h -FILE: ../../../third_party/libcxx/include/__ranges/single_view.h -FILE: ../../../third_party/libcxx/include/__ranges/size.h -FILE: ../../../third_party/libcxx/include/__ranges/subrange.h -FILE: ../../../third_party/libcxx/include/__ranges/take_view.h -FILE: ../../../third_party/libcxx/include/__ranges/transform_view.h -FILE: ../../../third_party/libcxx/include/__ranges/view_interface.h -FILE: ../../../third_party/libcxx/include/__ranges/views.h -FILE: ../../../third_party/libcxx/include/__ranges/zip_view.h -FILE: ../../../third_party/libcxx/include/__split_buffer -FILE: ../../../third_party/libcxx/include/__std_stream -FILE: ../../../third_party/libcxx/include/__string/char_traits.h -FILE: ../../../third_party/libcxx/include/__string/extern_template_lists.h -FILE: ../../../third_party/libcxx/include/__support/android/locale_bionic.h -FILE: ../../../third_party/libcxx/include/__support/fuchsia/xlocale.h -FILE: ../../../third_party/libcxx/include/__support/ibm/gettod_zos.h -FILE: ../../../third_party/libcxx/include/__support/ibm/limits.h -FILE: ../../../third_party/libcxx/include/__support/ibm/locale_mgmt_zos.h -FILE: ../../../third_party/libcxx/include/__support/ibm/nanosleep.h -FILE: ../../../third_party/libcxx/include/__support/ibm/support.h -FILE: ../../../third_party/libcxx/include/__support/ibm/xlocale.h -FILE: ../../../third_party/libcxx/include/__support/musl/xlocale.h -FILE: ../../../third_party/libcxx/include/__support/newlib/xlocale.h -FILE: ../../../third_party/libcxx/include/__support/openbsd/xlocale.h -FILE: ../../../third_party/libcxx/include/__support/solaris/floatingpoint.h -FILE: ../../../third_party/libcxx/include/__support/solaris/wchar.h -FILE: ../../../third_party/libcxx/include/__support/solaris/xlocale.h -FILE: ../../../third_party/libcxx/include/__support/win32/limits_msvc_win32.h -FILE: ../../../third_party/libcxx/include/__support/win32/locale_win32.h -FILE: ../../../third_party/libcxx/include/__support/xlocale/__nop_locale_mgmt.h -FILE: ../../../third_party/libcxx/include/__support/xlocale/__posix_l_fallback.h -FILE: ../../../third_party/libcxx/include/__support/xlocale/__strtonum_fallback.h -FILE: ../../../third_party/libcxx/include/__thread/poll_with_backoff.h -FILE: ../../../third_party/libcxx/include/__thread/timed_backoff_policy.h -FILE: ../../../third_party/libcxx/include/__threading_support -FILE: ../../../third_party/libcxx/include/__tree -FILE: ../../../third_party/libcxx/include/__tuple -FILE: ../../../third_party/libcxx/include/__type_traits/add_const.h -FILE: ../../../third_party/libcxx/include/__type_traits/add_cv.h -FILE: ../../../third_party/libcxx/include/__type_traits/add_lvalue_reference.h -FILE: ../../../third_party/libcxx/include/__type_traits/add_pointer.h -FILE: ../../../third_party/libcxx/include/__type_traits/add_rvalue_reference.h -FILE: ../../../third_party/libcxx/include/__type_traits/add_volatile.h -FILE: ../../../third_party/libcxx/include/__type_traits/alignment_of.h -FILE: ../../../third_party/libcxx/include/__type_traits/apply_cv.h -FILE: ../../../third_party/libcxx/include/__type_traits/conditional.h -FILE: ../../../third_party/libcxx/include/__type_traits/conjunction.h -FILE: ../../../third_party/libcxx/include/__type_traits/decay.h -FILE: ../../../third_party/libcxx/include/__type_traits/disjunction.h -FILE: ../../../third_party/libcxx/include/__type_traits/enable_if.h -FILE: ../../../third_party/libcxx/include/__type_traits/extent.h -FILE: ../../../third_party/libcxx/include/__type_traits/has_unique_object_representation.h -FILE: ../../../third_party/libcxx/include/__type_traits/has_virtual_destructor.h -FILE: ../../../third_party/libcxx/include/__type_traits/integral_constant.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_abstract.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_aggregate.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_arithmetic.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_array.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_assignable.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_base_of.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_bounded_array.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_callable.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_class.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_compound.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_const.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_constant_evaluated.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_constructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_convertible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_copy_assignable.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_copy_constructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_core_convertible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_default_constructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_destructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_empty.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_enum.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_final.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_floating_point.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_function.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_fundamental.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_integral.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_literal_type.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_member_function_pointer.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_member_object_pointer.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_member_pointer.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_move_assignable.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_move_constructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_assignable.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_constructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_copy_assignable.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_copy_constructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_default_constructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_destructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_move_assignable.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_move_constructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_null_pointer.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_object.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_pod.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_pointer.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_polymorphic.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_reference.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_reference_wrapper.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_referenceable.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_same.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_scalar.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_scoped_enum.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_signed.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_standard_layout.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_trivial.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_assignable.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_constructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_copy_assignable.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_copy_constructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_copyable.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_default_constructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_destructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_move_assignable.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_move_constructible.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_unbounded_array.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_union.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_unsigned.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_void.h -FILE: ../../../third_party/libcxx/include/__type_traits/is_volatile.h -FILE: ../../../third_party/libcxx/include/__type_traits/negation.h -FILE: ../../../third_party/libcxx/include/__type_traits/rank.h -FILE: ../../../third_party/libcxx/include/__type_traits/remove_all_extents.h -FILE: ../../../third_party/libcxx/include/__type_traits/remove_const.h -FILE: ../../../third_party/libcxx/include/__type_traits/remove_cv.h -FILE: ../../../third_party/libcxx/include/__type_traits/remove_extent.h -FILE: ../../../third_party/libcxx/include/__type_traits/remove_pointer.h -FILE: ../../../third_party/libcxx/include/__type_traits/remove_reference.h -FILE: ../../../third_party/libcxx/include/__type_traits/remove_volatile.h -FILE: ../../../third_party/libcxx/include/__type_traits/type_identity.h -FILE: ../../../third_party/libcxx/include/__type_traits/underlying_type.h -FILE: ../../../third_party/libcxx/include/__type_traits/void_t.h -FILE: ../../../third_party/libcxx/include/__undef_macros -FILE: ../../../third_party/libcxx/include/__utility/as_const.h -FILE: ../../../third_party/libcxx/include/__utility/auto_cast.h -FILE: ../../../third_party/libcxx/include/__utility/cmp.h -FILE: ../../../third_party/libcxx/include/__utility/declval.h -FILE: ../../../third_party/libcxx/include/__utility/exchange.h -FILE: ../../../third_party/libcxx/include/__utility/forward.h -FILE: ../../../third_party/libcxx/include/__utility/in_place.h -FILE: ../../../third_party/libcxx/include/__utility/integer_sequence.h -FILE: ../../../third_party/libcxx/include/__utility/move.h -FILE: ../../../third_party/libcxx/include/__utility/pair.h -FILE: ../../../third_party/libcxx/include/__utility/piecewise_construct.h -FILE: ../../../third_party/libcxx/include/__utility/priority_tag.h -FILE: ../../../third_party/libcxx/include/__utility/rel_ops.h -FILE: ../../../third_party/libcxx/include/__utility/swap.h -FILE: ../../../third_party/libcxx/include/__utility/to_underlying.h -FILE: ../../../third_party/libcxx/include/__utility/transaction.h -FILE: ../../../third_party/libcxx/include/__utility/unreachable.h -FILE: ../../../third_party/libcxx/include/__variant/monostate.h -FILE: ../../../third_party/libcxx/include/algorithm -FILE: ../../../third_party/libcxx/include/any -FILE: ../../../third_party/libcxx/include/array -FILE: ../../../third_party/libcxx/include/atomic -FILE: ../../../third_party/libcxx/include/barrier -FILE: ../../../third_party/libcxx/include/bit -FILE: ../../../third_party/libcxx/include/bitset -FILE: ../../../third_party/libcxx/include/cassert -FILE: ../../../third_party/libcxx/include/ccomplex -FILE: ../../../third_party/libcxx/include/cctype -FILE: ../../../third_party/libcxx/include/cerrno -FILE: ../../../third_party/libcxx/include/cfenv -FILE: ../../../third_party/libcxx/include/cfloat -FILE: ../../../third_party/libcxx/include/charconv -FILE: ../../../third_party/libcxx/include/chrono -FILE: ../../../third_party/libcxx/include/cinttypes -FILE: ../../../third_party/libcxx/include/ciso646 -FILE: ../../../third_party/libcxx/include/climits -FILE: ../../../third_party/libcxx/include/clocale -FILE: ../../../third_party/libcxx/include/cmath -FILE: ../../../third_party/libcxx/include/codecvt -FILE: ../../../third_party/libcxx/include/compare -FILE: ../../../third_party/libcxx/include/complex -FILE: ../../../third_party/libcxx/include/complex.h -FILE: ../../../third_party/libcxx/include/concepts -FILE: ../../../third_party/libcxx/include/condition_variable -FILE: ../../../third_party/libcxx/include/coroutine -FILE: ../../../third_party/libcxx/include/csetjmp -FILE: ../../../third_party/libcxx/include/csignal -FILE: ../../../third_party/libcxx/include/cstdarg -FILE: ../../../third_party/libcxx/include/cstdbool -FILE: ../../../third_party/libcxx/include/cstddef -FILE: ../../../third_party/libcxx/include/cstdint -FILE: ../../../third_party/libcxx/include/cstdio -FILE: ../../../third_party/libcxx/include/cstdlib -FILE: ../../../third_party/libcxx/include/cstring -FILE: ../../../third_party/libcxx/include/ctgmath -FILE: ../../../third_party/libcxx/include/ctime -FILE: ../../../third_party/libcxx/include/ctype.h -FILE: ../../../third_party/libcxx/include/cuchar -FILE: ../../../third_party/libcxx/include/cwchar -FILE: ../../../third_party/libcxx/include/cwctype -FILE: ../../../third_party/libcxx/include/deque -FILE: ../../../third_party/libcxx/include/errno.h -FILE: ../../../third_party/libcxx/include/exception -FILE: ../../../third_party/libcxx/include/execution -FILE: ../../../third_party/libcxx/include/experimental/__config -FILE: ../../../third_party/libcxx/include/experimental/__memory -FILE: ../../../third_party/libcxx/include/experimental/algorithm -FILE: ../../../third_party/libcxx/include/experimental/coroutine -FILE: ../../../third_party/libcxx/include/experimental/deque -FILE: ../../../third_party/libcxx/include/experimental/forward_list -FILE: ../../../third_party/libcxx/include/experimental/functional -FILE: ../../../third_party/libcxx/include/experimental/iterator -FILE: ../../../third_party/libcxx/include/experimental/list -FILE: ../../../third_party/libcxx/include/experimental/map -FILE: ../../../third_party/libcxx/include/experimental/memory_resource -FILE: ../../../third_party/libcxx/include/experimental/propagate_const -FILE: ../../../third_party/libcxx/include/experimental/regex -FILE: ../../../third_party/libcxx/include/experimental/set -FILE: ../../../third_party/libcxx/include/experimental/simd -FILE: ../../../third_party/libcxx/include/experimental/string -FILE: ../../../third_party/libcxx/include/experimental/type_traits -FILE: ../../../third_party/libcxx/include/experimental/unordered_map -FILE: ../../../third_party/libcxx/include/experimental/unordered_set -FILE: ../../../third_party/libcxx/include/experimental/utility -FILE: ../../../third_party/libcxx/include/experimental/vector -FILE: ../../../third_party/libcxx/include/ext/__hash -FILE: ../../../third_party/libcxx/include/ext/hash_map -FILE: ../../../third_party/libcxx/include/ext/hash_set -FILE: ../../../third_party/libcxx/include/fenv.h -FILE: ../../../third_party/libcxx/include/filesystem -FILE: ../../../third_party/libcxx/include/float.h -FILE: ../../../third_party/libcxx/include/format -FILE: ../../../third_party/libcxx/include/forward_list -FILE: ../../../third_party/libcxx/include/fstream -FILE: ../../../third_party/libcxx/include/functional -FILE: ../../../third_party/libcxx/include/future -FILE: ../../../third_party/libcxx/include/initializer_list -FILE: ../../../third_party/libcxx/include/inttypes.h -FILE: ../../../third_party/libcxx/include/iomanip -FILE: ../../../third_party/libcxx/include/ios -FILE: ../../../third_party/libcxx/include/iosfwd -FILE: ../../../third_party/libcxx/include/iostream -FILE: ../../../third_party/libcxx/include/istream -FILE: ../../../third_party/libcxx/include/iterator -FILE: ../../../third_party/libcxx/include/latch -FILE: ../../../third_party/libcxx/include/limits -FILE: ../../../third_party/libcxx/include/limits.h -FILE: ../../../third_party/libcxx/include/list -FILE: ../../../third_party/libcxx/include/locale -FILE: ../../../third_party/libcxx/include/locale.h -FILE: ../../../third_party/libcxx/include/map -FILE: ../../../third_party/libcxx/include/math.h -FILE: ../../../third_party/libcxx/include/memory -FILE: ../../../third_party/libcxx/include/mutex -FILE: ../../../third_party/libcxx/include/new -FILE: ../../../third_party/libcxx/include/numbers -FILE: ../../../third_party/libcxx/include/numeric -FILE: ../../../third_party/libcxx/include/optional -FILE: ../../../third_party/libcxx/include/ostream -FILE: ../../../third_party/libcxx/include/queue -FILE: ../../../third_party/libcxx/include/random -FILE: ../../../third_party/libcxx/include/ranges -FILE: ../../../third_party/libcxx/include/ratio -FILE: ../../../third_party/libcxx/include/regex -FILE: ../../../third_party/libcxx/include/scoped_allocator -FILE: ../../../third_party/libcxx/include/semaphore -FILE: ../../../third_party/libcxx/include/set -FILE: ../../../third_party/libcxx/include/setjmp.h -FILE: ../../../third_party/libcxx/include/shared_mutex -FILE: ../../../third_party/libcxx/include/span -FILE: ../../../third_party/libcxx/include/sstream -FILE: ../../../third_party/libcxx/include/stack -FILE: ../../../third_party/libcxx/include/stdatomic.h -FILE: ../../../third_party/libcxx/include/stdbool.h -FILE: ../../../third_party/libcxx/include/stddef.h -FILE: ../../../third_party/libcxx/include/stdexcept -FILE: ../../../third_party/libcxx/include/stdint.h -FILE: ../../../third_party/libcxx/include/stdio.h -FILE: ../../../third_party/libcxx/include/stdlib.h -FILE: ../../../third_party/libcxx/include/streambuf -FILE: ../../../third_party/libcxx/include/string -FILE: ../../../third_party/libcxx/include/string.h -FILE: ../../../third_party/libcxx/include/string_view -FILE: ../../../third_party/libcxx/include/strstream -FILE: ../../../third_party/libcxx/include/system_error -FILE: ../../../third_party/libcxx/include/tgmath.h -FILE: ../../../third_party/libcxx/include/thread -FILE: ../../../third_party/libcxx/include/tuple -FILE: ../../../third_party/libcxx/include/type_traits -FILE: ../../../third_party/libcxx/include/typeindex -FILE: ../../../third_party/libcxx/include/typeinfo -FILE: ../../../third_party/libcxx/include/uchar.h -FILE: ../../../third_party/libcxx/include/unordered_map -FILE: ../../../third_party/libcxx/include/unordered_set -FILE: ../../../third_party/libcxx/include/utility -FILE: ../../../third_party/libcxx/include/valarray -FILE: ../../../third_party/libcxx/include/variant -FILE: ../../../third_party/libcxx/include/vector -FILE: ../../../third_party/libcxx/include/wchar.h -FILE: ../../../third_party/libcxx/include/wctype.h -FILE: ../../../third_party/libcxx/src/algorithm.cpp -FILE: ../../../third_party/libcxx/src/any.cpp -FILE: ../../../third_party/libcxx/src/assert.cpp -FILE: ../../../third_party/libcxx/src/atomic.cpp -FILE: ../../../third_party/libcxx/src/barrier.cpp -FILE: ../../../third_party/libcxx/src/bind.cpp -FILE: ../../../third_party/libcxx/src/charconv.cpp -FILE: ../../../third_party/libcxx/src/chrono.cpp -FILE: ../../../third_party/libcxx/src/condition_variable.cpp -FILE: ../../../third_party/libcxx/src/condition_variable_destructor.cpp -FILE: ../../../third_party/libcxx/src/debug.cpp -FILE: ../../../third_party/libcxx/src/exception.cpp -FILE: ../../../third_party/libcxx/src/experimental/memory_resource.cpp -FILE: ../../../third_party/libcxx/src/filesystem/directory_iterator.cpp -FILE: ../../../third_party/libcxx/src/filesystem/filesystem_common.h -FILE: ../../../third_party/libcxx/src/filesystem/int128_builtins.cpp -FILE: ../../../third_party/libcxx/src/filesystem/operations.cpp -FILE: ../../../third_party/libcxx/src/filesystem/posix_compat.h -FILE: ../../../third_party/libcxx/src/format.cpp -FILE: ../../../third_party/libcxx/src/functional.cpp -FILE: ../../../third_party/libcxx/src/future.cpp -FILE: ../../../third_party/libcxx/src/hash.cpp -FILE: ../../../third_party/libcxx/src/include/apple_availability.h -FILE: ../../../third_party/libcxx/src/include/atomic_support.h -FILE: ../../../third_party/libcxx/src/include/config_elast.h -FILE: ../../../third_party/libcxx/src/include/refstring.h -FILE: ../../../third_party/libcxx/src/include/ryu/common.h -FILE: ../../../third_party/libcxx/src/include/ryu/d2fixed.h -FILE: ../../../third_party/libcxx/src/include/ryu/d2fixed_full_table.h -FILE: ../../../third_party/libcxx/src/include/ryu/d2s.h -FILE: ../../../third_party/libcxx/src/include/ryu/d2s_full_table.h -FILE: ../../../third_party/libcxx/src/include/ryu/d2s_intrinsics.h -FILE: ../../../third_party/libcxx/src/include/ryu/digit_table.h -FILE: ../../../third_party/libcxx/src/include/ryu/f2s.h -FILE: ../../../third_party/libcxx/src/include/ryu/ryu.h -FILE: ../../../third_party/libcxx/src/include/sso_allocator.h -FILE: ../../../third_party/libcxx/src/include/to_chars_floating_point.h -FILE: ../../../third_party/libcxx/src/ios.cpp -FILE: ../../../third_party/libcxx/src/ios.instantiations.cpp -FILE: ../../../third_party/libcxx/src/iostream.cpp -FILE: ../../../third_party/libcxx/src/legacy_debug_handler.cpp -FILE: ../../../third_party/libcxx/src/legacy_pointer_safety.cpp -FILE: ../../../third_party/libcxx/src/locale.cpp -FILE: ../../../third_party/libcxx/src/memory.cpp -FILE: ../../../third_party/libcxx/src/mutex.cpp -FILE: ../../../third_party/libcxx/src/mutex_destructor.cpp -FILE: ../../../third_party/libcxx/src/new.cpp -FILE: ../../../third_party/libcxx/src/optional.cpp -FILE: ../../../third_party/libcxx/src/random.cpp -FILE: ../../../third_party/libcxx/src/random_shuffle.cpp -FILE: ../../../third_party/libcxx/src/regex.cpp -FILE: ../../../third_party/libcxx/src/ryu/d2fixed.cpp -FILE: ../../../third_party/libcxx/src/ryu/d2s.cpp -FILE: ../../../third_party/libcxx/src/ryu/f2s.cpp -FILE: ../../../third_party/libcxx/src/shared_mutex.cpp -FILE: ../../../third_party/libcxx/src/stdexcept.cpp -FILE: ../../../third_party/libcxx/src/string.cpp -FILE: ../../../third_party/libcxx/src/strstream.cpp -FILE: ../../../third_party/libcxx/src/support/ibm/mbsnrtowcs.cpp -FILE: ../../../third_party/libcxx/src/support/ibm/wcsnrtombs.cpp -FILE: ../../../third_party/libcxx/src/support/ibm/xlocale_zos.cpp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_fallback.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_glibcxx.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_libcxxabi.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_libcxxrt.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_msvc.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_cxxabi.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_glibcxx.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_msvc.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_unimplemented.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/new_handler_fallback.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/stdexcept_default.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/stdexcept_vcruntime.ipp -FILE: ../../../third_party/libcxx/src/support/win32/locale_win32.cpp -FILE: ../../../third_party/libcxx/src/support/win32/support.cpp -FILE: ../../../third_party/libcxx/src/support/win32/thread_win32.cpp -FILE: ../../../third_party/libcxx/src/system_error.cpp -FILE: ../../../third_party/libcxx/src/thread.cpp -FILE: ../../../third_party/libcxx/src/typeinfo.cpp -FILE: ../../../third_party/libcxx/src/utility.cpp -FILE: ../../../third_party/libcxx/src/valarray.cpp -FILE: ../../../third_party/libcxx/src/variant.cpp -FILE: ../../../third_party/libcxx/src/vector.cpp -FILE: ../../../third_party/libcxxabi/include/__cxxabi_config.h -FILE: ../../../third_party/libcxxabi/include/cxxabi.h -FILE: ../../../third_party/libcxxabi/src/abort_message.cpp -FILE: ../../../third_party/libcxxabi/src/abort_message.h -FILE: ../../../third_party/libcxxabi/src/aix_state_tab_eh.inc -FILE: ../../../third_party/libcxxabi/src/cxa_aux_runtime.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_default_handlers.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_demangle.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_exception.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_exception.h -FILE: ../../../third_party/libcxxabi/src/cxa_exception_storage.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_guard.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_guard_impl.h -FILE: ../../../third_party/libcxxabi/src/cxa_handlers.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_handlers.h -FILE: ../../../third_party/libcxxabi/src/cxa_noexception.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_personality.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_thread_atexit.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_vector.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_virtual.cpp -FILE: ../../../third_party/libcxxabi/src/demangle/DemangleConfig.h -FILE: ../../../third_party/libcxxabi/src/demangle/ItaniumDemangle.h -FILE: ../../../third_party/libcxxabi/src/demangle/ItaniumNodes.def -FILE: ../../../third_party/libcxxabi/src/demangle/StringView.h -FILE: ../../../third_party/libcxxabi/src/demangle/Utility.h -FILE: ../../../third_party/libcxxabi/src/fallback_malloc.cpp -FILE: ../../../third_party/libcxxabi/src/fallback_malloc.h -FILE: ../../../third_party/libcxxabi/src/private_typeinfo.cpp -FILE: ../../../third_party/libcxxabi/src/private_typeinfo.h -FILE: ../../../third_party/libcxxabi/src/stdlib_exception.cpp -FILE: ../../../third_party/libcxxabi/src/stdlib_new_delete.cpp -FILE: ../../../third_party/libcxxabi/src/stdlib_stdexcept.cpp -FILE: ../../../third_party/libcxxabi/src/stdlib_typeinfo.cpp ----------------------------------------------------------------------------------------------------- -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. - - ---- LLVM Exceptions to the Apache 2.0 License ---- - -As an exception, if, as a result of your compiling your source code, portions -of this Software are embedded into an Object form of such source code, you -may redistribute such embedded portions in such Object form without complying -with the conditions of Sections 4(a), 4(b) and 4(d) of the License. - -In addition, if you combine or link compiled forms of this Software with -software that is licensed under the GPLv2 ("Combined Software") and if a -court of competent jurisdiction determines that the patent provision (Section -3), the indemnity provision (Section 9) or other Section of the License -conflicts with the conditions of the GPLv2, you may retroactively and -prospectively choose to deem waived or otherwise exclude such Section(s) of -the License, but only in their entirety and only with respect to the Combined -Software. -==================================================================================================== - -==================================================================================================== -LIBRARY: zlib -ORIGIN: ../../../third_party/zlib/zlib.h -TYPE: LicenseType.zlib -FILE: ../../../third_party/zlib/adler32.c -FILE: ../../../third_party/zlib/compress.c -FILE: ../../../third_party/zlib/contrib/optimizations/inffast_chunk.c -FILE: ../../../third_party/zlib/contrib/optimizations/inffast_chunk.h -FILE: ../../../third_party/zlib/contrib/optimizations/inflate.c -FILE: ../../../third_party/zlib/crc32.c -FILE: ../../../third_party/zlib/crc_folding.c -FILE: ../../../third_party/zlib/deflate.c -FILE: ../../../third_party/zlib/deflate.h -FILE: ../../../third_party/zlib/gzclose.c -FILE: ../../../third_party/zlib/gzguts.h -FILE: ../../../third_party/zlib/gzlib.c -FILE: ../../../third_party/zlib/gzread.c -FILE: ../../../third_party/zlib/gzwrite.c -FILE: ../../../third_party/zlib/infback.c -FILE: ../../../third_party/zlib/inffast.c -FILE: ../../../third_party/zlib/inffast.h -FILE: ../../../third_party/zlib/inflate.c -FILE: ../../../third_party/zlib/inflate.h -FILE: ../../../third_party/zlib/inftrees.c -FILE: ../../../third_party/zlib/inftrees.h -FILE: ../../../third_party/zlib/trees.c -FILE: ../../../third_party/zlib/uncompr.c -FILE: ../../../third_party/zlib/zconf.h -FILE: ../../../third_party/zlib/zconf.h.cmakein -FILE: ../../../third_party/zlib/zlib.h -FILE: ../../../third_party/zlib/zutil.c -FILE: ../../../third_party/zlib/zutil.h ----------------------------------------------------------------------------------------------------- -Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: zlib -ORIGIN: ../../../third_party/zlib/contrib/minizip/crypt.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/zlib/contrib/minizip/crypt.h ----------------------------------------------------------------------------------------------------- -Copyright (C) 1998-2005 Gilles Vollant -==================================================================================================== - -==================================================================================================== -LIBRARY: zlib -ORIGIN: ../../../third_party/zlib/contrib/optimizations/chunkcopy.h + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/zlib/contrib/optimizations/chunkcopy.h ----------------------------------------------------------------------------------------------------- -Copyright (C) 2017 ARM, Inc. -Copyright 2017 The Chromium Authors - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: libcxx -LIBRARY: libcxxabi -ORIGIN: ../../../third_party/libcxx/LICENSE.TXT -ORIGIN: ../../../third_party/libcxxabi/LICENSE.TXT -TYPE: LicenseType.mit -FILE: ../../../third_party/libcxx/include/module.modulemap.in -FILE: ../../../third_party/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.nodebug.noincomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.nodebug.noincomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.debug.noincomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.nodebug.incomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.debug.incomplete.abilist -FILE: ../../../third_party/libcxx/lib/libc++abi.exp -FILE: ../../../third_party/libcxx/lib/libc++unexp.exp -FILE: ../../../third_party/libcxx/lib/notweak.exp -FILE: ../../../third_party/libcxx/lib/weak.exp -FILE: ../../../third_party/libcxx/src/chrono_system_time_init.h -FILE: ../../../third_party/libcxx/src/experimental/memory_resource_init_helper.h -FILE: ../../../third_party/libcxx/src/iostream_init.h -FILE: ../../../third_party/libcxxabi/fuzz/cxa_demangle_fuzzer.cpp -FILE: ../../../third_party/libcxxabi/lib/exceptions.exp -FILE: ../../../third_party/libcxxabi/lib/itanium-base.exp -FILE: ../../../third_party/libcxxabi/lib/new-delete.exp -FILE: ../../../third_party/libcxxabi/lib/personality-sjlj.exp -FILE: ../../../third_party/libcxxabi/lib/personality-v0.exp ----------------------------------------------------------------------------------------------------- -Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: libcxx -LIBRARY: libcxxabi -ORIGIN: ../../../third_party/libcxx/LICENSE.TXT -ORIGIN: ../../../third_party/libcxxabi/LICENSE.TXT -TYPE: LicenseType.bsd -FILE: ../../../third_party/libcxx/include/module.modulemap.in -FILE: ../../../third_party/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.nodebug.noincomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.nodebug.noincomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.debug.noincomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.nodebug.incomplete.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.debug.incomplete.abilist -FILE: ../../../third_party/libcxx/lib/libc++abi.exp -FILE: ../../../third_party/libcxx/lib/libc++unexp.exp -FILE: ../../../third_party/libcxx/lib/notweak.exp -FILE: ../../../third_party/libcxx/lib/weak.exp -FILE: ../../../third_party/libcxx/src/chrono_system_time_init.h -FILE: ../../../third_party/libcxx/src/experimental/memory_resource_init_helper.h -FILE: ../../../third_party/libcxx/src/iostream_init.h -FILE: ../../../third_party/libcxxabi/fuzz/cxa_demangle_fuzzer.cpp -FILE: ../../../third_party/libcxxabi/lib/exceptions.exp -FILE: ../../../third_party/libcxxabi/lib/itanium-base.exp -FILE: ../../../third_party/libcxxabi/lib/new-delete.exp -FILE: ../../../third_party/libcxxabi/lib/personality-sjlj.exp -FILE: ../../../third_party/libcxxabi/lib/personality-v0.exp ----------------------------------------------------------------------------------------------------- -Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT - -All rights reserved. - -Developed by: - - LLVM Team - - University of Illinois at Urbana-Champaign - - http://llvm.org - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal with -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimers. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimers in the - documentation and/or other materials provided with the distribution. - - * Neither the names of the LLVM Team, University of Illinois at - Urbana-Champaign, nor the names of its contributors may be used to - endorse or promote products derived from this Software without specific - prior written permission. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE -SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: zlib -ORIGIN: ../../../third_party/zlib/adler32_simd.c + ../../../LICENSE -ORIGIN: ../../../third_party/zlib/adler32_simd.h + ../../../LICENSE -ORIGIN: ../../../third_party/zlib/crc32_simd.c + ../../../LICENSE -ORIGIN: ../../../third_party/zlib/crc32_simd.h + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/zlib/adler32_simd.c -FILE: ../../../third_party/zlib/adler32_simd.h -FILE: ../../../third_party/zlib/crc32_simd.c -FILE: ../../../third_party/zlib/crc32_simd.h ----------------------------------------------------------------------------------------------------- -Copyright 2017 The Chromium Authors - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: zlib -ORIGIN: ../../../third_party/zlib/contrib/bench/zlib_bench.cc + ../../../LICENSE -ORIGIN: ../../../third_party/zlib/cpu_features.c + ../../../LICENSE -ORIGIN: ../../../third_party/zlib/cpu_features.h + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/zlib/contrib/bench/zlib_bench.cc -FILE: ../../../third_party/zlib/cpu_features.c -FILE: ../../../third_party/zlib/cpu_features.h ----------------------------------------------------------------------------------------------------- -Copyright 2018 The Chromium Authors - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: libcxx -ORIGIN: ../../../third_party/libcxx/src/include/ryu/common.h -ORIGIN: ../../../third_party/libcxx/src/include/ryu/d2fixed.h -ORIGIN: ../../../third_party/libcxx/src/include/ryu/d2fixed_full_table.h -ORIGIN: ../../../third_party/libcxx/src/include/ryu/d2s.h -ORIGIN: ../../../third_party/libcxx/src/include/ryu/d2s_full_table.h -ORIGIN: ../../../third_party/libcxx/src/include/ryu/d2s_intrinsics.h -ORIGIN: ../../../third_party/libcxx/src/include/ryu/digit_table.h -ORIGIN: ../../../third_party/libcxx/src/include/ryu/f2s.h -ORIGIN: ../../../third_party/libcxx/src/include/ryu/ryu.h -ORIGIN: ../../../third_party/libcxx/src/ryu/d2fixed.cpp -ORIGIN: ../../../third_party/libcxx/src/ryu/d2s.cpp -ORIGIN: ../../../third_party/libcxx/src/ryu/f2s.cpp -TYPE: LicenseType.unknown -FILE: ../../../third_party/libcxx/src/include/ryu/common.h -FILE: ../../../third_party/libcxx/src/include/ryu/d2fixed.h -FILE: ../../../third_party/libcxx/src/include/ryu/d2fixed_full_table.h -FILE: ../../../third_party/libcxx/src/include/ryu/d2s.h -FILE: ../../../third_party/libcxx/src/include/ryu/d2s_full_table.h -FILE: ../../../third_party/libcxx/src/include/ryu/d2s_intrinsics.h -FILE: ../../../third_party/libcxx/src/include/ryu/digit_table.h -FILE: ../../../third_party/libcxx/src/include/ryu/f2s.h -FILE: ../../../third_party/libcxx/src/include/ryu/ryu.h -FILE: ../../../third_party/libcxx/src/ryu/d2fixed.cpp -FILE: ../../../third_party/libcxx/src/ryu/d2s.cpp -FILE: ../../../third_party/libcxx/src/ryu/f2s.cpp ----------------------------------------------------------------------------------------------------- -Copyright 2018 Ulf Adams -Copyright (c) Microsoft Corporation. All rights reserved. - -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: zlib -ORIGIN: ../../../third_party/zlib/contrib/optimizations/insert_string.h + ../../../LICENSE -ORIGIN: ../../../third_party/zlib/google/compression_utils_portable.cc + ../../../LICENSE -ORIGIN: ../../../third_party/zlib/google/compression_utils_portable.h + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/zlib/contrib/optimizations/insert_string.h -FILE: ../../../third_party/zlib/google/compression_utils_portable.cc -FILE: ../../../third_party/zlib/google/compression_utils_portable.h ----------------------------------------------------------------------------------------------------- -Copyright 2019 The Chromium Authors - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: zlib -ORIGIN: ../../../third_party/zlib/slide_hash_simd.h + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/zlib/slide_hash_simd.h ----------------------------------------------------------------------------------------------------- -Copyright 2022 The Chromium Authors - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: zlib -ORIGIN: ../../../third_party/zlib/contrib/minizip/unzip.h -ORIGIN: ../../../third_party/zlib/contrib/minizip/zip.h -TYPE: LicenseType.zlib -FILE: ../../../third_party/zlib/contrib/minizip/unzip.h -FILE: ../../../third_party/zlib/contrib/minizip/zip.h ----------------------------------------------------------------------------------------------------- -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: zlib -ORIGIN: ../../../third_party/zlib/LICENSE -TYPE: LicenseType.zlib -FILE: ../../../third_party/zlib/chromeconf.h -FILE: ../../../third_party/zlib/contrib/minizip/ChangeLogUnzip -FILE: ../../../third_party/zlib/contrib/minizip/ioapi.c -FILE: ../../../third_party/zlib/contrib/minizip/ioapi.h -FILE: ../../../third_party/zlib/contrib/minizip/iowin32.c -FILE: ../../../third_party/zlib/contrib/minizip/iowin32.h -FILE: ../../../third_party/zlib/contrib/minizip/mztools.c -FILE: ../../../third_party/zlib/contrib/minizip/mztools.h -FILE: ../../../third_party/zlib/contrib/minizip/unzip.c -FILE: ../../../third_party/zlib/contrib/minizip/unzip.h -FILE: ../../../third_party/zlib/contrib/minizip/zip.c -FILE: ../../../third_party/zlib/contrib/minizip/zip.h -FILE: ../../../third_party/zlib/crc32.h -FILE: ../../../third_party/zlib/google/compression_utils.cc -FILE: ../../../third_party/zlib/google/compression_utils.h -FILE: ../../../third_party/zlib/google/redact.h -FILE: ../../../third_party/zlib/google/test_data.filelist -FILE: ../../../third_party/zlib/google/test_data.globlist -FILE: ../../../third_party/zlib/google/zip.cc -FILE: ../../../third_party/zlib/google/zip.h -FILE: ../../../third_party/zlib/google/zip_internal.cc -FILE: ../../../third_party/zlib/google/zip_internal.h -FILE: ../../../third_party/zlib/google/zip_reader.cc -FILE: ../../../third_party/zlib/google/zip_reader.h -FILE: ../../../third_party/zlib/google/zip_writer.cc -FILE: ../../../third_party/zlib/google/zip_writer.h -FILE: ../../../third_party/zlib/inffixed.h -FILE: ../../../third_party/zlib/patches/0000-build.patch -FILE: ../../../third_party/zlib/patches/0001-simd.patch -FILE: ../../../third_party/zlib/patches/0002-uninitializedcheck.patch -FILE: ../../../third_party/zlib/patches/0003-uninitializedjump.patch -FILE: ../../../third_party/zlib/patches/0004-fix-uwp.patch -FILE: ../../../third_party/zlib/patches/0005-infcover-gtest.patch -FILE: ../../../third_party/zlib/patches/0006-fix-check_match.patch -FILE: ../../../third_party/zlib/patches/0007-zero-init-deflate-window.patch -FILE: ../../../third_party/zlib/patches/0008-minizip-zip-unzip-tools.patch -FILE: ../../../third_party/zlib/patches/0009-infcover-oob.patch -FILE: ../../../third_party/zlib/patches/0010-cmake-enable-simd.patch -FILE: ../../../third_party/zlib/patches/0011-avx512.patch -FILE: ../../../third_party/zlib/trees.h -FILE: ../../../third_party/zlib/zlib.map -FILE: ../../../third_party/zlib/zlib.pc.cmakein ----------------------------------------------------------------------------------------------------- -version 1.2.12, March 27th, 2022 - -Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -==================================================================================================== - -Total license count: 13 +Signature: be8ce23f4fb161952d9fa6424a6bf111 + +==================================================================================================== +LIBRARY: libcxx +LIBRARY: libcxxabi +ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/charconv.cpp +ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/common.h +ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/d2fixed.h +ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/d2fixed_full_table.h +ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/d2s.h +ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/d2s_full_table.h +ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/d2s_intrinsics.h +ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/digit_table.h +ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/f2s.h +ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/ryu/ryu.h +ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/include/to_chars_floating_point.h +ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/ryu/d2fixed.cpp +ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/ryu/d2s.cpp +ORIGIN: Apache-2.0 WITH LLVM-exception referenced by ../../../third_party/libcxx/src/ryu/f2s.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/adjacent_find.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/all_of.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/any_of.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/binary_search.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/clamp.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/comp.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/comp_ref_type.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/copy.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/copy_backward.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/copy_if.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/copy_n.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/count.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/count_if.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/equal.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/equal_range.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/fill.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/fill_n.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/find.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/find_end.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/find_first_of.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/find_if.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/find_if_not.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/for_each.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/for_each_n.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/generate.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/generate_n.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/half_positive.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/in_found_result.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/in_fun_result.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/in_in_out_result.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/in_in_result.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/in_out_out_result.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/in_out_result.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/includes.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/inplace_merge.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/is_heap.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/is_heap_until.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/is_partitioned.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/is_permutation.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/is_sorted.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/is_sorted_until.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/iter_swap.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/iterator_operations.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/lexicographical_compare.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/lower_bound.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/make_heap.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/make_projected.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/max.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/max_element.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/merge.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/min.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/min_element.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/min_max_result.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/minmax.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/minmax_element.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/mismatch.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/move.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/move_backward.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/next_permutation.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/none_of.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/nth_element.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/partial_sort.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/partial_sort_copy.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/partition.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/partition_copy.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/partition_point.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/pop_heap.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/prev_permutation.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/push_heap.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_adjacent_find.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_all_of.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_any_of.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_binary_search.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_copy.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_copy_backward.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_copy_if.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_copy_n.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_count.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_count_if.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_equal.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_fill.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_fill_n.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_find.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_find_first_of.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_find_if.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_find_if_not.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_for_each.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_for_each_n.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_is_partitioned.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_is_sorted.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_is_sorted_until.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_lexicographical_compare.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_lower_bound.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_max.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_max_element.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_merge.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_min.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_min_element.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_minmax.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_minmax_element.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_mismatch.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_move.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_move_backward.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_none_of.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_nth_element.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_remove.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_remove_if.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_replace.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_replace_if.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_reverse.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_set_difference.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_sort.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_stable_sort.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_swap_ranges.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_transform.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/ranges_upper_bound.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/remove.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/remove_copy.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/remove_copy_if.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/remove_if.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/replace.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/replace_copy.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/replace_copy_if.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/replace_if.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/reverse.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/reverse_copy.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/rotate.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/rotate_copy.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/sample.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/search.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/search_n.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/set_difference.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/set_intersection.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/set_symmetric_difference.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/set_union.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/shift_left.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/shift_right.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/shuffle.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/sift_down.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/sort.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/sort_heap.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/stable_partition.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/stable_sort.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/swap_ranges.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/transform.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/unique.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/unique_copy.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/unwrap_iter.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__algorithm/upper_bound.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__assert +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__availability +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__bit/bit_cast.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__bit/byteswap.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__bit_reference +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__bits +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__bsd_locale_defaults.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__bsd_locale_fallbacks.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__charconv/chars_format.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__charconv/from_chars_result.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__charconv/tables.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__charconv/to_chars_base_10.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__charconv/to_chars_result.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/calendar.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/convert_to_timespec.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/day.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/duration.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/file_clock.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/hh_mm_ss.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/high_resolution_clock.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/literals.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/month.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/month_weekday.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/monthday.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/steady_clock.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/system_clock.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/time_point.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/weekday.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/year.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/year_month.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/year_month_day.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__chrono/year_month_weekday.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/common_comparison_category.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/compare_partial_order_fallback.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/compare_strong_order_fallback.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/compare_three_way.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/compare_three_way_result.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/compare_weak_order_fallback.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/is_eq.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/ordering.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/partial_order.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/strong_order.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/synth_three_way.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/three_way_comparable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__compare/weak_order.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/arithmetic.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/assignable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/boolean_testable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/class_or_enum.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/common_reference_with.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/common_with.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/constructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/convertible_to.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/copyable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/derived_from.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/destructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/different_from.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/equality_comparable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/invocable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/movable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/predicate.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/regular.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/relation.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/same_as.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/semiregular.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/swappable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__concepts/totally_ordered.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__config +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__config_site.in +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__coroutine/coroutine_handle.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__coroutine/coroutine_traits.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__coroutine/noop_coroutine_handle.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__coroutine/trivial_awaitables.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__debug +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__debug_utils/randomize_range.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__errc +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/copy_options.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/directory_entry.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/directory_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/directory_options.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/file_status.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/file_time_type.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/file_type.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/filesystem_error.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/operations.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/path.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/path_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/perm_options.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/perms.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/recursive_directory_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/space_info.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__filesystem/u8path.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/buffer.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/concepts.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/enable_insertable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_arg.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_arg_store.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_args.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_context.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_error.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_fwd.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_parse_context.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_string.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/format_to_n_result.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_bool.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_char.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_floating_point.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_integer.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_integral.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_output.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_pointer.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/formatter_string.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__format/parser_std_format_spec.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/binary_function.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/binary_negate.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/bind.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/bind_back.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/bind_front.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/binder1st.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/binder2nd.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/boyer_moore_searcher.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/compose.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/default_searcher.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/function.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/hash.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/identity.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/invoke.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/is_transparent.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/mem_fn.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/mem_fun_ref.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/not_fn.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/operations.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/perfect_forward.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/pointer_to_binary_function.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/pointer_to_unary_function.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/ranges_operations.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/reference_wrapper.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/unary_function.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/unary_negate.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/unwrap_ref.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__functional/weak_result_type.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__fwd/span.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__fwd/string_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__hash_table +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ios/fpos.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/access.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/advance.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/back_insert_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/bounded_iter.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/common_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/concepts.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/counted_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/data.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/default_sentinel.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/distance.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/empty.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/erase_if_container.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/front_insert_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/incrementable_traits.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/indirectly_comparable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/insert_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/istream_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/istreambuf_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/iter_move.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/iter_swap.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/iterator_traits.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/mergeable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/move_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/move_sentinel.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/next.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/ostream_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/ostreambuf_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/permutable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/prev.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/projected.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/readable_traits.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/reverse_access.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/reverse_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/size.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/sortable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/unreachable_sentinel.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__iterator/wrap_iter.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__locale +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__mbstate_t.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/addressof.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/allocate_at_least.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/allocation_guard.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/allocator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/allocator_arg_t.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/allocator_traits.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/assume_aligned.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/auto_ptr.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/compressed_pair.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/concepts.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/construct_at.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/pointer_traits.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/ranges_construct_at.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/ranges_uninitialized_algorithms.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/raw_storage_iterator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/shared_ptr.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/temporary_buffer.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/uninitialized_algorithms.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/unique_ptr.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/uses_allocator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__memory/voidify.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__mutex_base +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__node_handle +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/accumulate.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/adjacent_difference.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/exclusive_scan.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/gcd_lcm.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/inclusive_scan.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/inner_product.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/iota.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/midpoint.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/partial_sum.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/reduce.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/transform_exclusive_scan.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/transform_inclusive_scan.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__numeric/transform_reduce.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/bernoulli_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/binomial_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/cauchy_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/chi_squared_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/clamp_to_integral.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/default_random_engine.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/discard_block_engine.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/discrete_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/exponential_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/extreme_value_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/fisher_f_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/gamma_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/generate_canonical.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/geometric_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/independent_bits_engine.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/is_seed_sequence.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/is_valid.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/knuth_b.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/linear_congruential_engine.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/log2.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/lognormal_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/mersenne_twister_engine.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/negative_binomial_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/normal_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/piecewise_constant_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/piecewise_linear_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/poisson_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/random_device.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/ranlux.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/seed_seq.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/shuffle_order_engine.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/student_t_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/subtract_with_carry_engine.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/uniform_int_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/uniform_random_bit_generator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/uniform_real_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__random/weibull_distribution.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/access.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/all.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/common_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/concepts.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/copyable_box.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/counted.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/dangling.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/data.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/drop_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/empty.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/empty_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/enable_borrowed_range.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/enable_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/filter_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/iota_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/join_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/lazy_split_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/non_propagating_cache.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/owning_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/range_adaptor.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/rbegin.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/ref_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/rend.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/reverse_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/single_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/size.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/subrange.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/take_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/transform_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/view_interface.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/views.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__ranges/zip_view.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__split_buffer +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__std_stream +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__string/char_traits.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__string/extern_template_lists.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/android/locale_bionic.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/fuchsia/xlocale.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/ibm/gettod_zos.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/ibm/limits.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/ibm/locale_mgmt_zos.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/ibm/nanosleep.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/ibm/support.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/ibm/xlocale.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/musl/xlocale.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/newlib/xlocale.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/openbsd/xlocale.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/solaris/floatingpoint.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/solaris/wchar.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/solaris/xlocale.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/win32/limits_msvc_win32.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/win32/locale_win32.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/xlocale/__nop_locale_mgmt.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/xlocale/__posix_l_fallback.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__support/xlocale/__strtonum_fallback.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__thread/poll_with_backoff.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__thread/timed_backoff_policy.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__threading_support +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__tree +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__tuple +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/add_const.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/add_cv.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/add_lvalue_reference.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/add_pointer.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/add_rvalue_reference.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/add_volatile.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/alignment_of.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/apply_cv.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/conditional.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/conjunction.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/decay.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/disjunction.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/enable_if.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/extent.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/has_unique_object_representation.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/has_virtual_destructor.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/integral_constant.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_abstract.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_aggregate.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_arithmetic.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_array.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_assignable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_base_of.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_bounded_array.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_callable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_class.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_compound.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_const.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_constant_evaluated.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_constructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_convertible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_copy_assignable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_copy_constructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_core_convertible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_default_constructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_destructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_empty.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_enum.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_final.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_floating_point.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_function.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_fundamental.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_integral.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_literal_type.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_member_function_pointer.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_member_object_pointer.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_member_pointer.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_move_assignable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_move_constructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_assignable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_constructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_copy_assignable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_copy_constructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_default_constructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_destructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_move_assignable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_nothrow_move_constructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_null_pointer.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_object.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_pod.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_pointer.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_polymorphic.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_reference.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_reference_wrapper.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_referenceable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_same.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_scalar.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_scoped_enum.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_signed.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_standard_layout.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivial.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_assignable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_constructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_copy_assignable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_copy_constructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_copyable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_default_constructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_destructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_move_assignable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_trivially_move_constructible.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_unbounded_array.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_union.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_unsigned.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_void.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/is_volatile.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/negation.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/rank.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/remove_all_extents.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/remove_const.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/remove_cv.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/remove_extent.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/remove_pointer.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/remove_reference.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/remove_volatile.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/type_identity.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/underlying_type.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__type_traits/void_t.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__undef_macros +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/as_const.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/auto_cast.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/cmp.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/declval.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/exchange.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/forward.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/in_place.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/integer_sequence.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/move.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/pair.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/piecewise_construct.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/priority_tag.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/rel_ops.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/swap.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/to_underlying.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/transaction.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__utility/unreachable.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/__variant/monostate.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/algorithm +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/any +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/array +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/atomic +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/barrier +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/bit +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/bitset +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cassert +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ccomplex +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cctype +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cerrno +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cfenv +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cfloat +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/charconv +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/chrono +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cinttypes +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ciso646 +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/climits +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/clocale +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cmath +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/codecvt +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/compare +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/complex +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/complex.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/concepts +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/condition_variable +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/coroutine +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/csetjmp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/csignal +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cstdarg +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cstdbool +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cstddef +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cstdint +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cstdio +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cstdlib +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cstring +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ctgmath +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ctime +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ctype.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cuchar +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cwchar +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/cwctype +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/deque +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/errno.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/exception +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/execution +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/__config +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/__memory +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/algorithm +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/coroutine +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/deque +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/forward_list +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/functional +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/iterator +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/list +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/map +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/memory_resource +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/propagate_const +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/regex +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/set +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/simd +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/string +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/type_traits +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/unordered_map +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/unordered_set +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/utility +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/experimental/vector +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ext/__hash +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ext/hash_map +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ext/hash_set +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/fenv.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/filesystem +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/float.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/format +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/forward_list +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/fstream +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/functional +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/future +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/initializer_list +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/inttypes.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/iomanip +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ios +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/iosfwd +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/iostream +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/istream +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/iterator +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/latch +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/limits +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/limits.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/list +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/locale +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/locale.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/map +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/math.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/memory +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/mutex +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/new +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/numbers +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/numeric +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/optional +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ostream +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/queue +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/random +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ranges +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/ratio +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/regex +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/scoped_allocator +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/semaphore +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/set +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/setjmp.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/shared_mutex +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/span +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/sstream +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stack +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stdatomic.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stdbool.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stddef.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stdexcept +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stdint.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stdio.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/stdlib.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/streambuf +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/string +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/string.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/string_view +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/strstream +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/system_error +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/tgmath.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/thread +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/tuple +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/type_traits +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/typeindex +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/typeinfo +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/uchar.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/unordered_map +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/unordered_set +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/utility +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/valarray +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/variant +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/vector +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/wchar.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/include/wctype.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/algorithm.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/any.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/assert.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/atomic.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/barrier.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/bind.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/charconv.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/chrono.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/condition_variable.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/condition_variable_destructor.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/debug.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/exception.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/experimental/memory_resource.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/filesystem/directory_iterator.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/filesystem/filesystem_common.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/filesystem/int128_builtins.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/filesystem/operations.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/filesystem/posix_compat.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/format.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/functional.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/future.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/hash.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/apple_availability.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/atomic_support.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/config_elast.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/refstring.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/common.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/d2fixed.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/d2fixed_full_table.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/d2s.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/d2s_full_table.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/d2s_intrinsics.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/digit_table.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/f2s.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/ryu/ryu.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/sso_allocator.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/include/to_chars_floating_point.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/ios.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/ios.instantiations.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/iostream.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/legacy_debug_handler.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/legacy_pointer_safety.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/locale.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/memory.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/mutex.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/mutex_destructor.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/new.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/optional.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/random.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/random_shuffle.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/regex.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/ryu/d2fixed.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/ryu/d2s.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/ryu/f2s.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/shared_mutex.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/stdexcept.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/string.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/strstream.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/ibm/mbsnrtowcs.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/ibm/wcsnrtombs.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/ibm/xlocale_zos.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_fallback.ipp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_glibcxx.ipp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_libcxxabi.ipp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_libcxxrt.ipp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_msvc.ipp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_pointer_cxxabi.ipp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_pointer_glibcxx.ipp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_pointer_msvc.ipp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/exception_pointer_unimplemented.ipp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/new_handler_fallback.ipp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/stdexcept_default.ipp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/runtime/stdexcept_vcruntime.ipp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/win32/locale_win32.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/win32/support.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/support/win32/thread_win32.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/system_error.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/thread.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/typeinfo.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/utility.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/valarray.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/variant.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxx/src/vector.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/include/__cxxabi_config.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/include/cxxabi.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/abort_message.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/abort_message.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/aix_state_tab_eh.inc +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_aux_runtime.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_default_handlers.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_demangle.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_exception.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_exception.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_exception_storage.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_guard.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_guard_impl.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_handlers.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_handlers.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_noexception.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_personality.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_thread_atexit.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_vector.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/cxa_virtual.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/demangle/DemangleConfig.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/demangle/ItaniumDemangle.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/demangle/ItaniumNodes.def +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/demangle/StringView.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/demangle/Utility.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/fallback_malloc.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/fallback_malloc.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/private_typeinfo.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/private_typeinfo.h +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/stdlib_exception.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/stdlib_new_delete.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/stdlib_stdexcept.cpp +ORIGIN: https://llvm.org/LICENSE.txt referenced by ../../../third_party/libcxxabi/src/stdlib_typeinfo.cpp +TYPE: LicenseType.llvm +FILE: ../../../third_party/libcxx/include/__algorithm/adjacent_find.h +FILE: ../../../third_party/libcxx/include/__algorithm/all_of.h +FILE: ../../../third_party/libcxx/include/__algorithm/any_of.h +FILE: ../../../third_party/libcxx/include/__algorithm/binary_search.h +FILE: ../../../third_party/libcxx/include/__algorithm/clamp.h +FILE: ../../../third_party/libcxx/include/__algorithm/comp.h +FILE: ../../../third_party/libcxx/include/__algorithm/comp_ref_type.h +FILE: ../../../third_party/libcxx/include/__algorithm/copy.h +FILE: ../../../third_party/libcxx/include/__algorithm/copy_backward.h +FILE: ../../../third_party/libcxx/include/__algorithm/copy_if.h +FILE: ../../../third_party/libcxx/include/__algorithm/copy_n.h +FILE: ../../../third_party/libcxx/include/__algorithm/count.h +FILE: ../../../third_party/libcxx/include/__algorithm/count_if.h +FILE: ../../../third_party/libcxx/include/__algorithm/equal.h +FILE: ../../../third_party/libcxx/include/__algorithm/equal_range.h +FILE: ../../../third_party/libcxx/include/__algorithm/fill.h +FILE: ../../../third_party/libcxx/include/__algorithm/fill_n.h +FILE: ../../../third_party/libcxx/include/__algorithm/find.h +FILE: ../../../third_party/libcxx/include/__algorithm/find_end.h +FILE: ../../../third_party/libcxx/include/__algorithm/find_first_of.h +FILE: ../../../third_party/libcxx/include/__algorithm/find_if.h +FILE: ../../../third_party/libcxx/include/__algorithm/find_if_not.h +FILE: ../../../third_party/libcxx/include/__algorithm/for_each.h +FILE: ../../../third_party/libcxx/include/__algorithm/for_each_n.h +FILE: ../../../third_party/libcxx/include/__algorithm/generate.h +FILE: ../../../third_party/libcxx/include/__algorithm/generate_n.h +FILE: ../../../third_party/libcxx/include/__algorithm/half_positive.h +FILE: ../../../third_party/libcxx/include/__algorithm/in_found_result.h +FILE: ../../../third_party/libcxx/include/__algorithm/in_fun_result.h +FILE: ../../../third_party/libcxx/include/__algorithm/in_in_out_result.h +FILE: ../../../third_party/libcxx/include/__algorithm/in_in_result.h +FILE: ../../../third_party/libcxx/include/__algorithm/in_out_out_result.h +FILE: ../../../third_party/libcxx/include/__algorithm/in_out_result.h +FILE: ../../../third_party/libcxx/include/__algorithm/includes.h +FILE: ../../../third_party/libcxx/include/__algorithm/inplace_merge.h +FILE: ../../../third_party/libcxx/include/__algorithm/is_heap.h +FILE: ../../../third_party/libcxx/include/__algorithm/is_heap_until.h +FILE: ../../../third_party/libcxx/include/__algorithm/is_partitioned.h +FILE: ../../../third_party/libcxx/include/__algorithm/is_permutation.h +FILE: ../../../third_party/libcxx/include/__algorithm/is_sorted.h +FILE: ../../../third_party/libcxx/include/__algorithm/is_sorted_until.h +FILE: ../../../third_party/libcxx/include/__algorithm/iter_swap.h +FILE: ../../../third_party/libcxx/include/__algorithm/iterator_operations.h +FILE: ../../../third_party/libcxx/include/__algorithm/lexicographical_compare.h +FILE: ../../../third_party/libcxx/include/__algorithm/lower_bound.h +FILE: ../../../third_party/libcxx/include/__algorithm/make_heap.h +FILE: ../../../third_party/libcxx/include/__algorithm/make_projected.h +FILE: ../../../third_party/libcxx/include/__algorithm/max.h +FILE: ../../../third_party/libcxx/include/__algorithm/max_element.h +FILE: ../../../third_party/libcxx/include/__algorithm/merge.h +FILE: ../../../third_party/libcxx/include/__algorithm/min.h +FILE: ../../../third_party/libcxx/include/__algorithm/min_element.h +FILE: ../../../third_party/libcxx/include/__algorithm/min_max_result.h +FILE: ../../../third_party/libcxx/include/__algorithm/minmax.h +FILE: ../../../third_party/libcxx/include/__algorithm/minmax_element.h +FILE: ../../../third_party/libcxx/include/__algorithm/mismatch.h +FILE: ../../../third_party/libcxx/include/__algorithm/move.h +FILE: ../../../third_party/libcxx/include/__algorithm/move_backward.h +FILE: ../../../third_party/libcxx/include/__algorithm/next_permutation.h +FILE: ../../../third_party/libcxx/include/__algorithm/none_of.h +FILE: ../../../third_party/libcxx/include/__algorithm/nth_element.h +FILE: ../../../third_party/libcxx/include/__algorithm/partial_sort.h +FILE: ../../../third_party/libcxx/include/__algorithm/partial_sort_copy.h +FILE: ../../../third_party/libcxx/include/__algorithm/partition.h +FILE: ../../../third_party/libcxx/include/__algorithm/partition_copy.h +FILE: ../../../third_party/libcxx/include/__algorithm/partition_point.h +FILE: ../../../third_party/libcxx/include/__algorithm/pop_heap.h +FILE: ../../../third_party/libcxx/include/__algorithm/prev_permutation.h +FILE: ../../../third_party/libcxx/include/__algorithm/push_heap.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_adjacent_find.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_all_of.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_any_of.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_binary_search.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_copy.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_copy_backward.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_copy_if.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_copy_n.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_count.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_count_if.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_equal.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_fill.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_fill_n.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_find.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_find_first_of.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_find_if.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_find_if_not.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_for_each.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_for_each_n.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_is_partitioned.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_is_sorted.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_is_sorted_until.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_lexicographical_compare.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_lower_bound.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_max.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_max_element.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_merge.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_min.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_min_element.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_minmax.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_minmax_element.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_mismatch.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_move.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_move_backward.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_none_of.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_nth_element.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_remove.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_remove_if.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_replace.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_replace_if.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_reverse.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_set_difference.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_sort.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_stable_sort.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_swap_ranges.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_transform.h +FILE: ../../../third_party/libcxx/include/__algorithm/ranges_upper_bound.h +FILE: ../../../third_party/libcxx/include/__algorithm/remove.h +FILE: ../../../third_party/libcxx/include/__algorithm/remove_copy.h +FILE: ../../../third_party/libcxx/include/__algorithm/remove_copy_if.h +FILE: ../../../third_party/libcxx/include/__algorithm/remove_if.h +FILE: ../../../third_party/libcxx/include/__algorithm/replace.h +FILE: ../../../third_party/libcxx/include/__algorithm/replace_copy.h +FILE: ../../../third_party/libcxx/include/__algorithm/replace_copy_if.h +FILE: ../../../third_party/libcxx/include/__algorithm/replace_if.h +FILE: ../../../third_party/libcxx/include/__algorithm/reverse.h +FILE: ../../../third_party/libcxx/include/__algorithm/reverse_copy.h +FILE: ../../../third_party/libcxx/include/__algorithm/rotate.h +FILE: ../../../third_party/libcxx/include/__algorithm/rotate_copy.h +FILE: ../../../third_party/libcxx/include/__algorithm/sample.h +FILE: ../../../third_party/libcxx/include/__algorithm/search.h +FILE: ../../../third_party/libcxx/include/__algorithm/search_n.h +FILE: ../../../third_party/libcxx/include/__algorithm/set_difference.h +FILE: ../../../third_party/libcxx/include/__algorithm/set_intersection.h +FILE: ../../../third_party/libcxx/include/__algorithm/set_symmetric_difference.h +FILE: ../../../third_party/libcxx/include/__algorithm/set_union.h +FILE: ../../../third_party/libcxx/include/__algorithm/shift_left.h +FILE: ../../../third_party/libcxx/include/__algorithm/shift_right.h +FILE: ../../../third_party/libcxx/include/__algorithm/shuffle.h +FILE: ../../../third_party/libcxx/include/__algorithm/sift_down.h +FILE: ../../../third_party/libcxx/include/__algorithm/sort.h +FILE: ../../../third_party/libcxx/include/__algorithm/sort_heap.h +FILE: ../../../third_party/libcxx/include/__algorithm/stable_partition.h +FILE: ../../../third_party/libcxx/include/__algorithm/stable_sort.h +FILE: ../../../third_party/libcxx/include/__algorithm/swap_ranges.h +FILE: ../../../third_party/libcxx/include/__algorithm/transform.h +FILE: ../../../third_party/libcxx/include/__algorithm/unique.h +FILE: ../../../third_party/libcxx/include/__algorithm/unique_copy.h +FILE: ../../../third_party/libcxx/include/__algorithm/unwrap_iter.h +FILE: ../../../third_party/libcxx/include/__algorithm/upper_bound.h +FILE: ../../../third_party/libcxx/include/__assert +FILE: ../../../third_party/libcxx/include/__availability +FILE: ../../../third_party/libcxx/include/__bit/bit_cast.h +FILE: ../../../third_party/libcxx/include/__bit/byteswap.h +FILE: ../../../third_party/libcxx/include/__bit_reference +FILE: ../../../third_party/libcxx/include/__bits +FILE: ../../../third_party/libcxx/include/__bsd_locale_defaults.h +FILE: ../../../third_party/libcxx/include/__bsd_locale_fallbacks.h +FILE: ../../../third_party/libcxx/include/__charconv/chars_format.h +FILE: ../../../third_party/libcxx/include/__charconv/from_chars_result.h +FILE: ../../../third_party/libcxx/include/__charconv/tables.h +FILE: ../../../third_party/libcxx/include/__charconv/to_chars_base_10.h +FILE: ../../../third_party/libcxx/include/__charconv/to_chars_result.h +FILE: ../../../third_party/libcxx/include/__chrono/calendar.h +FILE: ../../../third_party/libcxx/include/__chrono/convert_to_timespec.h +FILE: ../../../third_party/libcxx/include/__chrono/day.h +FILE: ../../../third_party/libcxx/include/__chrono/duration.h +FILE: ../../../third_party/libcxx/include/__chrono/file_clock.h +FILE: ../../../third_party/libcxx/include/__chrono/hh_mm_ss.h +FILE: ../../../third_party/libcxx/include/__chrono/high_resolution_clock.h +FILE: ../../../third_party/libcxx/include/__chrono/literals.h +FILE: ../../../third_party/libcxx/include/__chrono/month.h +FILE: ../../../third_party/libcxx/include/__chrono/month_weekday.h +FILE: ../../../third_party/libcxx/include/__chrono/monthday.h +FILE: ../../../third_party/libcxx/include/__chrono/steady_clock.h +FILE: ../../../third_party/libcxx/include/__chrono/system_clock.h +FILE: ../../../third_party/libcxx/include/__chrono/time_point.h +FILE: ../../../third_party/libcxx/include/__chrono/weekday.h +FILE: ../../../third_party/libcxx/include/__chrono/year.h +FILE: ../../../third_party/libcxx/include/__chrono/year_month.h +FILE: ../../../third_party/libcxx/include/__chrono/year_month_day.h +FILE: ../../../third_party/libcxx/include/__chrono/year_month_weekday.h +FILE: ../../../third_party/libcxx/include/__compare/common_comparison_category.h +FILE: ../../../third_party/libcxx/include/__compare/compare_partial_order_fallback.h +FILE: ../../../third_party/libcxx/include/__compare/compare_strong_order_fallback.h +FILE: ../../../third_party/libcxx/include/__compare/compare_three_way.h +FILE: ../../../third_party/libcxx/include/__compare/compare_three_way_result.h +FILE: ../../../third_party/libcxx/include/__compare/compare_weak_order_fallback.h +FILE: ../../../third_party/libcxx/include/__compare/is_eq.h +FILE: ../../../third_party/libcxx/include/__compare/ordering.h +FILE: ../../../third_party/libcxx/include/__compare/partial_order.h +FILE: ../../../third_party/libcxx/include/__compare/strong_order.h +FILE: ../../../third_party/libcxx/include/__compare/synth_three_way.h +FILE: ../../../third_party/libcxx/include/__compare/three_way_comparable.h +FILE: ../../../third_party/libcxx/include/__compare/weak_order.h +FILE: ../../../third_party/libcxx/include/__concepts/arithmetic.h +FILE: ../../../third_party/libcxx/include/__concepts/assignable.h +FILE: ../../../third_party/libcxx/include/__concepts/boolean_testable.h +FILE: ../../../third_party/libcxx/include/__concepts/class_or_enum.h +FILE: ../../../third_party/libcxx/include/__concepts/common_reference_with.h +FILE: ../../../third_party/libcxx/include/__concepts/common_with.h +FILE: ../../../third_party/libcxx/include/__concepts/constructible.h +FILE: ../../../third_party/libcxx/include/__concepts/convertible_to.h +FILE: ../../../third_party/libcxx/include/__concepts/copyable.h +FILE: ../../../third_party/libcxx/include/__concepts/derived_from.h +FILE: ../../../third_party/libcxx/include/__concepts/destructible.h +FILE: ../../../third_party/libcxx/include/__concepts/different_from.h +FILE: ../../../third_party/libcxx/include/__concepts/equality_comparable.h +FILE: ../../../third_party/libcxx/include/__concepts/invocable.h +FILE: ../../../third_party/libcxx/include/__concepts/movable.h +FILE: ../../../third_party/libcxx/include/__concepts/predicate.h +FILE: ../../../third_party/libcxx/include/__concepts/regular.h +FILE: ../../../third_party/libcxx/include/__concepts/relation.h +FILE: ../../../third_party/libcxx/include/__concepts/same_as.h +FILE: ../../../third_party/libcxx/include/__concepts/semiregular.h +FILE: ../../../third_party/libcxx/include/__concepts/swappable.h +FILE: ../../../third_party/libcxx/include/__concepts/totally_ordered.h +FILE: ../../../third_party/libcxx/include/__config +FILE: ../../../third_party/libcxx/include/__config_site.in +FILE: ../../../third_party/libcxx/include/__coroutine/coroutine_handle.h +FILE: ../../../third_party/libcxx/include/__coroutine/coroutine_traits.h +FILE: ../../../third_party/libcxx/include/__coroutine/noop_coroutine_handle.h +FILE: ../../../third_party/libcxx/include/__coroutine/trivial_awaitables.h +FILE: ../../../third_party/libcxx/include/__debug +FILE: ../../../third_party/libcxx/include/__debug_utils/randomize_range.h +FILE: ../../../third_party/libcxx/include/__errc +FILE: ../../../third_party/libcxx/include/__filesystem/copy_options.h +FILE: ../../../third_party/libcxx/include/__filesystem/directory_entry.h +FILE: ../../../third_party/libcxx/include/__filesystem/directory_iterator.h +FILE: ../../../third_party/libcxx/include/__filesystem/directory_options.h +FILE: ../../../third_party/libcxx/include/__filesystem/file_status.h +FILE: ../../../third_party/libcxx/include/__filesystem/file_time_type.h +FILE: ../../../third_party/libcxx/include/__filesystem/file_type.h +FILE: ../../../third_party/libcxx/include/__filesystem/filesystem_error.h +FILE: ../../../third_party/libcxx/include/__filesystem/operations.h +FILE: ../../../third_party/libcxx/include/__filesystem/path.h +FILE: ../../../third_party/libcxx/include/__filesystem/path_iterator.h +FILE: ../../../third_party/libcxx/include/__filesystem/perm_options.h +FILE: ../../../third_party/libcxx/include/__filesystem/perms.h +FILE: ../../../third_party/libcxx/include/__filesystem/recursive_directory_iterator.h +FILE: ../../../third_party/libcxx/include/__filesystem/space_info.h +FILE: ../../../third_party/libcxx/include/__filesystem/u8path.h +FILE: ../../../third_party/libcxx/include/__format/buffer.h +FILE: ../../../third_party/libcxx/include/__format/concepts.h +FILE: ../../../third_party/libcxx/include/__format/enable_insertable.h +FILE: ../../../third_party/libcxx/include/__format/format_arg.h +FILE: ../../../third_party/libcxx/include/__format/format_arg_store.h +FILE: ../../../third_party/libcxx/include/__format/format_args.h +FILE: ../../../third_party/libcxx/include/__format/format_context.h +FILE: ../../../third_party/libcxx/include/__format/format_error.h +FILE: ../../../third_party/libcxx/include/__format/format_fwd.h +FILE: ../../../third_party/libcxx/include/__format/format_parse_context.h +FILE: ../../../third_party/libcxx/include/__format/format_string.h +FILE: ../../../third_party/libcxx/include/__format/format_to_n_result.h +FILE: ../../../third_party/libcxx/include/__format/formatter.h +FILE: ../../../third_party/libcxx/include/__format/formatter_bool.h +FILE: ../../../third_party/libcxx/include/__format/formatter_char.h +FILE: ../../../third_party/libcxx/include/__format/formatter_floating_point.h +FILE: ../../../third_party/libcxx/include/__format/formatter_integer.h +FILE: ../../../third_party/libcxx/include/__format/formatter_integral.h +FILE: ../../../third_party/libcxx/include/__format/formatter_output.h +FILE: ../../../third_party/libcxx/include/__format/formatter_pointer.h +FILE: ../../../third_party/libcxx/include/__format/formatter_string.h +FILE: ../../../third_party/libcxx/include/__format/parser_std_format_spec.h +FILE: ../../../third_party/libcxx/include/__functional/binary_function.h +FILE: ../../../third_party/libcxx/include/__functional/binary_negate.h +FILE: ../../../third_party/libcxx/include/__functional/bind.h +FILE: ../../../third_party/libcxx/include/__functional/bind_back.h +FILE: ../../../third_party/libcxx/include/__functional/bind_front.h +FILE: ../../../third_party/libcxx/include/__functional/binder1st.h +FILE: ../../../third_party/libcxx/include/__functional/binder2nd.h +FILE: ../../../third_party/libcxx/include/__functional/boyer_moore_searcher.h +FILE: ../../../third_party/libcxx/include/__functional/compose.h +FILE: ../../../third_party/libcxx/include/__functional/default_searcher.h +FILE: ../../../third_party/libcxx/include/__functional/function.h +FILE: ../../../third_party/libcxx/include/__functional/hash.h +FILE: ../../../third_party/libcxx/include/__functional/identity.h +FILE: ../../../third_party/libcxx/include/__functional/invoke.h +FILE: ../../../third_party/libcxx/include/__functional/is_transparent.h +FILE: ../../../third_party/libcxx/include/__functional/mem_fn.h +FILE: ../../../third_party/libcxx/include/__functional/mem_fun_ref.h +FILE: ../../../third_party/libcxx/include/__functional/not_fn.h +FILE: ../../../third_party/libcxx/include/__functional/operations.h +FILE: ../../../third_party/libcxx/include/__functional/perfect_forward.h +FILE: ../../../third_party/libcxx/include/__functional/pointer_to_binary_function.h +FILE: ../../../third_party/libcxx/include/__functional/pointer_to_unary_function.h +FILE: ../../../third_party/libcxx/include/__functional/ranges_operations.h +FILE: ../../../third_party/libcxx/include/__functional/reference_wrapper.h +FILE: ../../../third_party/libcxx/include/__functional/unary_function.h +FILE: ../../../third_party/libcxx/include/__functional/unary_negate.h +FILE: ../../../third_party/libcxx/include/__functional/unwrap_ref.h +FILE: ../../../third_party/libcxx/include/__functional/weak_result_type.h +FILE: ../../../third_party/libcxx/include/__fwd/span.h +FILE: ../../../third_party/libcxx/include/__fwd/string_view.h +FILE: ../../../third_party/libcxx/include/__hash_table +FILE: ../../../third_party/libcxx/include/__ios/fpos.h +FILE: ../../../third_party/libcxx/include/__iterator/access.h +FILE: ../../../third_party/libcxx/include/__iterator/advance.h +FILE: ../../../third_party/libcxx/include/__iterator/back_insert_iterator.h +FILE: ../../../third_party/libcxx/include/__iterator/bounded_iter.h +FILE: ../../../third_party/libcxx/include/__iterator/common_iterator.h +FILE: ../../../third_party/libcxx/include/__iterator/concepts.h +FILE: ../../../third_party/libcxx/include/__iterator/counted_iterator.h +FILE: ../../../third_party/libcxx/include/__iterator/data.h +FILE: ../../../third_party/libcxx/include/__iterator/default_sentinel.h +FILE: ../../../third_party/libcxx/include/__iterator/distance.h +FILE: ../../../third_party/libcxx/include/__iterator/empty.h +FILE: ../../../third_party/libcxx/include/__iterator/erase_if_container.h +FILE: ../../../third_party/libcxx/include/__iterator/front_insert_iterator.h +FILE: ../../../third_party/libcxx/include/__iterator/incrementable_traits.h +FILE: ../../../third_party/libcxx/include/__iterator/indirectly_comparable.h +FILE: ../../../third_party/libcxx/include/__iterator/insert_iterator.h +FILE: ../../../third_party/libcxx/include/__iterator/istream_iterator.h +FILE: ../../../third_party/libcxx/include/__iterator/istreambuf_iterator.h +FILE: ../../../third_party/libcxx/include/__iterator/iter_move.h +FILE: ../../../third_party/libcxx/include/__iterator/iter_swap.h +FILE: ../../../third_party/libcxx/include/__iterator/iterator.h +FILE: ../../../third_party/libcxx/include/__iterator/iterator_traits.h +FILE: ../../../third_party/libcxx/include/__iterator/mergeable.h +FILE: ../../../third_party/libcxx/include/__iterator/move_iterator.h +FILE: ../../../third_party/libcxx/include/__iterator/move_sentinel.h +FILE: ../../../third_party/libcxx/include/__iterator/next.h +FILE: ../../../third_party/libcxx/include/__iterator/ostream_iterator.h +FILE: ../../../third_party/libcxx/include/__iterator/ostreambuf_iterator.h +FILE: ../../../third_party/libcxx/include/__iterator/permutable.h +FILE: ../../../third_party/libcxx/include/__iterator/prev.h +FILE: ../../../third_party/libcxx/include/__iterator/projected.h +FILE: ../../../third_party/libcxx/include/__iterator/readable_traits.h +FILE: ../../../third_party/libcxx/include/__iterator/reverse_access.h +FILE: ../../../third_party/libcxx/include/__iterator/reverse_iterator.h +FILE: ../../../third_party/libcxx/include/__iterator/size.h +FILE: ../../../third_party/libcxx/include/__iterator/sortable.h +FILE: ../../../third_party/libcxx/include/__iterator/unreachable_sentinel.h +FILE: ../../../third_party/libcxx/include/__iterator/wrap_iter.h +FILE: ../../../third_party/libcxx/include/__locale +FILE: ../../../third_party/libcxx/include/__mbstate_t.h +FILE: ../../../third_party/libcxx/include/__memory/addressof.h +FILE: ../../../third_party/libcxx/include/__memory/allocate_at_least.h +FILE: ../../../third_party/libcxx/include/__memory/allocation_guard.h +FILE: ../../../third_party/libcxx/include/__memory/allocator.h +FILE: ../../../third_party/libcxx/include/__memory/allocator_arg_t.h +FILE: ../../../third_party/libcxx/include/__memory/allocator_traits.h +FILE: ../../../third_party/libcxx/include/__memory/assume_aligned.h +FILE: ../../../third_party/libcxx/include/__memory/auto_ptr.h +FILE: ../../../third_party/libcxx/include/__memory/compressed_pair.h +FILE: ../../../third_party/libcxx/include/__memory/concepts.h +FILE: ../../../third_party/libcxx/include/__memory/construct_at.h +FILE: ../../../third_party/libcxx/include/__memory/pointer_traits.h +FILE: ../../../third_party/libcxx/include/__memory/ranges_construct_at.h +FILE: ../../../third_party/libcxx/include/__memory/ranges_uninitialized_algorithms.h +FILE: ../../../third_party/libcxx/include/__memory/raw_storage_iterator.h +FILE: ../../../third_party/libcxx/include/__memory/shared_ptr.h +FILE: ../../../third_party/libcxx/include/__memory/temporary_buffer.h +FILE: ../../../third_party/libcxx/include/__memory/uninitialized_algorithms.h +FILE: ../../../third_party/libcxx/include/__memory/unique_ptr.h +FILE: ../../../third_party/libcxx/include/__memory/uses_allocator.h +FILE: ../../../third_party/libcxx/include/__memory/voidify.h +FILE: ../../../third_party/libcxx/include/__mutex_base +FILE: ../../../third_party/libcxx/include/__node_handle +FILE: ../../../third_party/libcxx/include/__numeric/accumulate.h +FILE: ../../../third_party/libcxx/include/__numeric/adjacent_difference.h +FILE: ../../../third_party/libcxx/include/__numeric/exclusive_scan.h +FILE: ../../../third_party/libcxx/include/__numeric/gcd_lcm.h +FILE: ../../../third_party/libcxx/include/__numeric/inclusive_scan.h +FILE: ../../../third_party/libcxx/include/__numeric/inner_product.h +FILE: ../../../third_party/libcxx/include/__numeric/iota.h +FILE: ../../../third_party/libcxx/include/__numeric/midpoint.h +FILE: ../../../third_party/libcxx/include/__numeric/partial_sum.h +FILE: ../../../third_party/libcxx/include/__numeric/reduce.h +FILE: ../../../third_party/libcxx/include/__numeric/transform_exclusive_scan.h +FILE: ../../../third_party/libcxx/include/__numeric/transform_inclusive_scan.h +FILE: ../../../third_party/libcxx/include/__numeric/transform_reduce.h +FILE: ../../../third_party/libcxx/include/__random/bernoulli_distribution.h +FILE: ../../../third_party/libcxx/include/__random/binomial_distribution.h +FILE: ../../../third_party/libcxx/include/__random/cauchy_distribution.h +FILE: ../../../third_party/libcxx/include/__random/chi_squared_distribution.h +FILE: ../../../third_party/libcxx/include/__random/clamp_to_integral.h +FILE: ../../../third_party/libcxx/include/__random/default_random_engine.h +FILE: ../../../third_party/libcxx/include/__random/discard_block_engine.h +FILE: ../../../third_party/libcxx/include/__random/discrete_distribution.h +FILE: ../../../third_party/libcxx/include/__random/exponential_distribution.h +FILE: ../../../third_party/libcxx/include/__random/extreme_value_distribution.h +FILE: ../../../third_party/libcxx/include/__random/fisher_f_distribution.h +FILE: ../../../third_party/libcxx/include/__random/gamma_distribution.h +FILE: ../../../third_party/libcxx/include/__random/generate_canonical.h +FILE: ../../../third_party/libcxx/include/__random/geometric_distribution.h +FILE: ../../../third_party/libcxx/include/__random/independent_bits_engine.h +FILE: ../../../third_party/libcxx/include/__random/is_seed_sequence.h +FILE: ../../../third_party/libcxx/include/__random/is_valid.h +FILE: ../../../third_party/libcxx/include/__random/knuth_b.h +FILE: ../../../third_party/libcxx/include/__random/linear_congruential_engine.h +FILE: ../../../third_party/libcxx/include/__random/log2.h +FILE: ../../../third_party/libcxx/include/__random/lognormal_distribution.h +FILE: ../../../third_party/libcxx/include/__random/mersenne_twister_engine.h +FILE: ../../../third_party/libcxx/include/__random/negative_binomial_distribution.h +FILE: ../../../third_party/libcxx/include/__random/normal_distribution.h +FILE: ../../../third_party/libcxx/include/__random/piecewise_constant_distribution.h +FILE: ../../../third_party/libcxx/include/__random/piecewise_linear_distribution.h +FILE: ../../../third_party/libcxx/include/__random/poisson_distribution.h +FILE: ../../../third_party/libcxx/include/__random/random_device.h +FILE: ../../../third_party/libcxx/include/__random/ranlux.h +FILE: ../../../third_party/libcxx/include/__random/seed_seq.h +FILE: ../../../third_party/libcxx/include/__random/shuffle_order_engine.h +FILE: ../../../third_party/libcxx/include/__random/student_t_distribution.h +FILE: ../../../third_party/libcxx/include/__random/subtract_with_carry_engine.h +FILE: ../../../third_party/libcxx/include/__random/uniform_int_distribution.h +FILE: ../../../third_party/libcxx/include/__random/uniform_random_bit_generator.h +FILE: ../../../third_party/libcxx/include/__random/uniform_real_distribution.h +FILE: ../../../third_party/libcxx/include/__random/weibull_distribution.h +FILE: ../../../third_party/libcxx/include/__ranges/access.h +FILE: ../../../third_party/libcxx/include/__ranges/all.h +FILE: ../../../third_party/libcxx/include/__ranges/common_view.h +FILE: ../../../third_party/libcxx/include/__ranges/concepts.h +FILE: ../../../third_party/libcxx/include/__ranges/copyable_box.h +FILE: ../../../third_party/libcxx/include/__ranges/counted.h +FILE: ../../../third_party/libcxx/include/__ranges/dangling.h +FILE: ../../../third_party/libcxx/include/__ranges/data.h +FILE: ../../../third_party/libcxx/include/__ranges/drop_view.h +FILE: ../../../third_party/libcxx/include/__ranges/empty.h +FILE: ../../../third_party/libcxx/include/__ranges/empty_view.h +FILE: ../../../third_party/libcxx/include/__ranges/enable_borrowed_range.h +FILE: ../../../third_party/libcxx/include/__ranges/enable_view.h +FILE: ../../../third_party/libcxx/include/__ranges/filter_view.h +FILE: ../../../third_party/libcxx/include/__ranges/iota_view.h +FILE: ../../../third_party/libcxx/include/__ranges/join_view.h +FILE: ../../../third_party/libcxx/include/__ranges/lazy_split_view.h +FILE: ../../../third_party/libcxx/include/__ranges/non_propagating_cache.h +FILE: ../../../third_party/libcxx/include/__ranges/owning_view.h +FILE: ../../../third_party/libcxx/include/__ranges/range_adaptor.h +FILE: ../../../third_party/libcxx/include/__ranges/rbegin.h +FILE: ../../../third_party/libcxx/include/__ranges/ref_view.h +FILE: ../../../third_party/libcxx/include/__ranges/rend.h +FILE: ../../../third_party/libcxx/include/__ranges/reverse_view.h +FILE: ../../../third_party/libcxx/include/__ranges/single_view.h +FILE: ../../../third_party/libcxx/include/__ranges/size.h +FILE: ../../../third_party/libcxx/include/__ranges/subrange.h +FILE: ../../../third_party/libcxx/include/__ranges/take_view.h +FILE: ../../../third_party/libcxx/include/__ranges/transform_view.h +FILE: ../../../third_party/libcxx/include/__ranges/view_interface.h +FILE: ../../../third_party/libcxx/include/__ranges/views.h +FILE: ../../../third_party/libcxx/include/__ranges/zip_view.h +FILE: ../../../third_party/libcxx/include/__split_buffer +FILE: ../../../third_party/libcxx/include/__std_stream +FILE: ../../../third_party/libcxx/include/__string/char_traits.h +FILE: ../../../third_party/libcxx/include/__string/extern_template_lists.h +FILE: ../../../third_party/libcxx/include/__support/android/locale_bionic.h +FILE: ../../../third_party/libcxx/include/__support/fuchsia/xlocale.h +FILE: ../../../third_party/libcxx/include/__support/ibm/gettod_zos.h +FILE: ../../../third_party/libcxx/include/__support/ibm/limits.h +FILE: ../../../third_party/libcxx/include/__support/ibm/locale_mgmt_zos.h +FILE: ../../../third_party/libcxx/include/__support/ibm/nanosleep.h +FILE: ../../../third_party/libcxx/include/__support/ibm/support.h +FILE: ../../../third_party/libcxx/include/__support/ibm/xlocale.h +FILE: ../../../third_party/libcxx/include/__support/musl/xlocale.h +FILE: ../../../third_party/libcxx/include/__support/newlib/xlocale.h +FILE: ../../../third_party/libcxx/include/__support/openbsd/xlocale.h +FILE: ../../../third_party/libcxx/include/__support/solaris/floatingpoint.h +FILE: ../../../third_party/libcxx/include/__support/solaris/wchar.h +FILE: ../../../third_party/libcxx/include/__support/solaris/xlocale.h +FILE: ../../../third_party/libcxx/include/__support/win32/limits_msvc_win32.h +FILE: ../../../third_party/libcxx/include/__support/win32/locale_win32.h +FILE: ../../../third_party/libcxx/include/__support/xlocale/__nop_locale_mgmt.h +FILE: ../../../third_party/libcxx/include/__support/xlocale/__posix_l_fallback.h +FILE: ../../../third_party/libcxx/include/__support/xlocale/__strtonum_fallback.h +FILE: ../../../third_party/libcxx/include/__thread/poll_with_backoff.h +FILE: ../../../third_party/libcxx/include/__thread/timed_backoff_policy.h +FILE: ../../../third_party/libcxx/include/__threading_support +FILE: ../../../third_party/libcxx/include/__tree +FILE: ../../../third_party/libcxx/include/__tuple +FILE: ../../../third_party/libcxx/include/__type_traits/add_const.h +FILE: ../../../third_party/libcxx/include/__type_traits/add_cv.h +FILE: ../../../third_party/libcxx/include/__type_traits/add_lvalue_reference.h +FILE: ../../../third_party/libcxx/include/__type_traits/add_pointer.h +FILE: ../../../third_party/libcxx/include/__type_traits/add_rvalue_reference.h +FILE: ../../../third_party/libcxx/include/__type_traits/add_volatile.h +FILE: ../../../third_party/libcxx/include/__type_traits/alignment_of.h +FILE: ../../../third_party/libcxx/include/__type_traits/apply_cv.h +FILE: ../../../third_party/libcxx/include/__type_traits/conditional.h +FILE: ../../../third_party/libcxx/include/__type_traits/conjunction.h +FILE: ../../../third_party/libcxx/include/__type_traits/decay.h +FILE: ../../../third_party/libcxx/include/__type_traits/disjunction.h +FILE: ../../../third_party/libcxx/include/__type_traits/enable_if.h +FILE: ../../../third_party/libcxx/include/__type_traits/extent.h +FILE: ../../../third_party/libcxx/include/__type_traits/has_unique_object_representation.h +FILE: ../../../third_party/libcxx/include/__type_traits/has_virtual_destructor.h +FILE: ../../../third_party/libcxx/include/__type_traits/integral_constant.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_abstract.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_aggregate.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_arithmetic.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_array.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_assignable.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_base_of.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_bounded_array.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_callable.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_class.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_compound.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_const.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_constant_evaluated.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_constructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_convertible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_copy_assignable.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_copy_constructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_core_convertible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_default_constructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_destructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_empty.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_enum.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_final.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_floating_point.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_function.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_fundamental.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_integral.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_literal_type.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_member_function_pointer.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_member_object_pointer.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_member_pointer.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_move_assignable.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_move_constructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_assignable.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_constructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_copy_assignable.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_copy_constructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_default_constructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_destructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_move_assignable.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_nothrow_move_constructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_null_pointer.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_object.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_pod.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_pointer.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_polymorphic.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_reference.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_reference_wrapper.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_referenceable.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_same.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_scalar.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_scoped_enum.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_signed.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_standard_layout.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_trivial.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_assignable.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_constructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_copy_assignable.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_copy_constructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_copyable.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_default_constructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_destructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_move_assignable.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_trivially_move_constructible.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_unbounded_array.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_union.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_unsigned.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_void.h +FILE: ../../../third_party/libcxx/include/__type_traits/is_volatile.h +FILE: ../../../third_party/libcxx/include/__type_traits/negation.h +FILE: ../../../third_party/libcxx/include/__type_traits/rank.h +FILE: ../../../third_party/libcxx/include/__type_traits/remove_all_extents.h +FILE: ../../../third_party/libcxx/include/__type_traits/remove_const.h +FILE: ../../../third_party/libcxx/include/__type_traits/remove_cv.h +FILE: ../../../third_party/libcxx/include/__type_traits/remove_extent.h +FILE: ../../../third_party/libcxx/include/__type_traits/remove_pointer.h +FILE: ../../../third_party/libcxx/include/__type_traits/remove_reference.h +FILE: ../../../third_party/libcxx/include/__type_traits/remove_volatile.h +FILE: ../../../third_party/libcxx/include/__type_traits/type_identity.h +FILE: ../../../third_party/libcxx/include/__type_traits/underlying_type.h +FILE: ../../../third_party/libcxx/include/__type_traits/void_t.h +FILE: ../../../third_party/libcxx/include/__undef_macros +FILE: ../../../third_party/libcxx/include/__utility/as_const.h +FILE: ../../../third_party/libcxx/include/__utility/auto_cast.h +FILE: ../../../third_party/libcxx/include/__utility/cmp.h +FILE: ../../../third_party/libcxx/include/__utility/declval.h +FILE: ../../../third_party/libcxx/include/__utility/exchange.h +FILE: ../../../third_party/libcxx/include/__utility/forward.h +FILE: ../../../third_party/libcxx/include/__utility/in_place.h +FILE: ../../../third_party/libcxx/include/__utility/integer_sequence.h +FILE: ../../../third_party/libcxx/include/__utility/move.h +FILE: ../../../third_party/libcxx/include/__utility/pair.h +FILE: ../../../third_party/libcxx/include/__utility/piecewise_construct.h +FILE: ../../../third_party/libcxx/include/__utility/priority_tag.h +FILE: ../../../third_party/libcxx/include/__utility/rel_ops.h +FILE: ../../../third_party/libcxx/include/__utility/swap.h +FILE: ../../../third_party/libcxx/include/__utility/to_underlying.h +FILE: ../../../third_party/libcxx/include/__utility/transaction.h +FILE: ../../../third_party/libcxx/include/__utility/unreachable.h +FILE: ../../../third_party/libcxx/include/__variant/monostate.h +FILE: ../../../third_party/libcxx/include/algorithm +FILE: ../../../third_party/libcxx/include/any +FILE: ../../../third_party/libcxx/include/array +FILE: ../../../third_party/libcxx/include/atomic +FILE: ../../../third_party/libcxx/include/barrier +FILE: ../../../third_party/libcxx/include/bit +FILE: ../../../third_party/libcxx/include/bitset +FILE: ../../../third_party/libcxx/include/cassert +FILE: ../../../third_party/libcxx/include/ccomplex +FILE: ../../../third_party/libcxx/include/cctype +FILE: ../../../third_party/libcxx/include/cerrno +FILE: ../../../third_party/libcxx/include/cfenv +FILE: ../../../third_party/libcxx/include/cfloat +FILE: ../../../third_party/libcxx/include/charconv +FILE: ../../../third_party/libcxx/include/chrono +FILE: ../../../third_party/libcxx/include/cinttypes +FILE: ../../../third_party/libcxx/include/ciso646 +FILE: ../../../third_party/libcxx/include/climits +FILE: ../../../third_party/libcxx/include/clocale +FILE: ../../../third_party/libcxx/include/cmath +FILE: ../../../third_party/libcxx/include/codecvt +FILE: ../../../third_party/libcxx/include/compare +FILE: ../../../third_party/libcxx/include/complex +FILE: ../../../third_party/libcxx/include/complex.h +FILE: ../../../third_party/libcxx/include/concepts +FILE: ../../../third_party/libcxx/include/condition_variable +FILE: ../../../third_party/libcxx/include/coroutine +FILE: ../../../third_party/libcxx/include/csetjmp +FILE: ../../../third_party/libcxx/include/csignal +FILE: ../../../third_party/libcxx/include/cstdarg +FILE: ../../../third_party/libcxx/include/cstdbool +FILE: ../../../third_party/libcxx/include/cstddef +FILE: ../../../third_party/libcxx/include/cstdint +FILE: ../../../third_party/libcxx/include/cstdio +FILE: ../../../third_party/libcxx/include/cstdlib +FILE: ../../../third_party/libcxx/include/cstring +FILE: ../../../third_party/libcxx/include/ctgmath +FILE: ../../../third_party/libcxx/include/ctime +FILE: ../../../third_party/libcxx/include/ctype.h +FILE: ../../../third_party/libcxx/include/cuchar +FILE: ../../../third_party/libcxx/include/cwchar +FILE: ../../../third_party/libcxx/include/cwctype +FILE: ../../../third_party/libcxx/include/deque +FILE: ../../../third_party/libcxx/include/errno.h +FILE: ../../../third_party/libcxx/include/exception +FILE: ../../../third_party/libcxx/include/execution +FILE: ../../../third_party/libcxx/include/experimental/__config +FILE: ../../../third_party/libcxx/include/experimental/__memory +FILE: ../../../third_party/libcxx/include/experimental/algorithm +FILE: ../../../third_party/libcxx/include/experimental/coroutine +FILE: ../../../third_party/libcxx/include/experimental/deque +FILE: ../../../third_party/libcxx/include/experimental/forward_list +FILE: ../../../third_party/libcxx/include/experimental/functional +FILE: ../../../third_party/libcxx/include/experimental/iterator +FILE: ../../../third_party/libcxx/include/experimental/list +FILE: ../../../third_party/libcxx/include/experimental/map +FILE: ../../../third_party/libcxx/include/experimental/memory_resource +FILE: ../../../third_party/libcxx/include/experimental/propagate_const +FILE: ../../../third_party/libcxx/include/experimental/regex +FILE: ../../../third_party/libcxx/include/experimental/set +FILE: ../../../third_party/libcxx/include/experimental/simd +FILE: ../../../third_party/libcxx/include/experimental/string +FILE: ../../../third_party/libcxx/include/experimental/type_traits +FILE: ../../../third_party/libcxx/include/experimental/unordered_map +FILE: ../../../third_party/libcxx/include/experimental/unordered_set +FILE: ../../../third_party/libcxx/include/experimental/utility +FILE: ../../../third_party/libcxx/include/experimental/vector +FILE: ../../../third_party/libcxx/include/ext/__hash +FILE: ../../../third_party/libcxx/include/ext/hash_map +FILE: ../../../third_party/libcxx/include/ext/hash_set +FILE: ../../../third_party/libcxx/include/fenv.h +FILE: ../../../third_party/libcxx/include/filesystem +FILE: ../../../third_party/libcxx/include/float.h +FILE: ../../../third_party/libcxx/include/format +FILE: ../../../third_party/libcxx/include/forward_list +FILE: ../../../third_party/libcxx/include/fstream +FILE: ../../../third_party/libcxx/include/functional +FILE: ../../../third_party/libcxx/include/future +FILE: ../../../third_party/libcxx/include/initializer_list +FILE: ../../../third_party/libcxx/include/inttypes.h +FILE: ../../../third_party/libcxx/include/iomanip +FILE: ../../../third_party/libcxx/include/ios +FILE: ../../../third_party/libcxx/include/iosfwd +FILE: ../../../third_party/libcxx/include/iostream +FILE: ../../../third_party/libcxx/include/istream +FILE: ../../../third_party/libcxx/include/iterator +FILE: ../../../third_party/libcxx/include/latch +FILE: ../../../third_party/libcxx/include/limits +FILE: ../../../third_party/libcxx/include/limits.h +FILE: ../../../third_party/libcxx/include/list +FILE: ../../../third_party/libcxx/include/locale +FILE: ../../../third_party/libcxx/include/locale.h +FILE: ../../../third_party/libcxx/include/map +FILE: ../../../third_party/libcxx/include/math.h +FILE: ../../../third_party/libcxx/include/memory +FILE: ../../../third_party/libcxx/include/mutex +FILE: ../../../third_party/libcxx/include/new +FILE: ../../../third_party/libcxx/include/numbers +FILE: ../../../third_party/libcxx/include/numeric +FILE: ../../../third_party/libcxx/include/optional +FILE: ../../../third_party/libcxx/include/ostream +FILE: ../../../third_party/libcxx/include/queue +FILE: ../../../third_party/libcxx/include/random +FILE: ../../../third_party/libcxx/include/ranges +FILE: ../../../third_party/libcxx/include/ratio +FILE: ../../../third_party/libcxx/include/regex +FILE: ../../../third_party/libcxx/include/scoped_allocator +FILE: ../../../third_party/libcxx/include/semaphore +FILE: ../../../third_party/libcxx/include/set +FILE: ../../../third_party/libcxx/include/setjmp.h +FILE: ../../../third_party/libcxx/include/shared_mutex +FILE: ../../../third_party/libcxx/include/span +FILE: ../../../third_party/libcxx/include/sstream +FILE: ../../../third_party/libcxx/include/stack +FILE: ../../../third_party/libcxx/include/stdatomic.h +FILE: ../../../third_party/libcxx/include/stdbool.h +FILE: ../../../third_party/libcxx/include/stddef.h +FILE: ../../../third_party/libcxx/include/stdexcept +FILE: ../../../third_party/libcxx/include/stdint.h +FILE: ../../../third_party/libcxx/include/stdio.h +FILE: ../../../third_party/libcxx/include/stdlib.h +FILE: ../../../third_party/libcxx/include/streambuf +FILE: ../../../third_party/libcxx/include/string +FILE: ../../../third_party/libcxx/include/string.h +FILE: ../../../third_party/libcxx/include/string_view +FILE: ../../../third_party/libcxx/include/strstream +FILE: ../../../third_party/libcxx/include/system_error +FILE: ../../../third_party/libcxx/include/tgmath.h +FILE: ../../../third_party/libcxx/include/thread +FILE: ../../../third_party/libcxx/include/tuple +FILE: ../../../third_party/libcxx/include/type_traits +FILE: ../../../third_party/libcxx/include/typeindex +FILE: ../../../third_party/libcxx/include/typeinfo +FILE: ../../../third_party/libcxx/include/uchar.h +FILE: ../../../third_party/libcxx/include/unordered_map +FILE: ../../../third_party/libcxx/include/unordered_set +FILE: ../../../third_party/libcxx/include/utility +FILE: ../../../third_party/libcxx/include/valarray +FILE: ../../../third_party/libcxx/include/variant +FILE: ../../../third_party/libcxx/include/vector +FILE: ../../../third_party/libcxx/include/wchar.h +FILE: ../../../third_party/libcxx/include/wctype.h +FILE: ../../../third_party/libcxx/src/algorithm.cpp +FILE: ../../../third_party/libcxx/src/any.cpp +FILE: ../../../third_party/libcxx/src/assert.cpp +FILE: ../../../third_party/libcxx/src/atomic.cpp +FILE: ../../../third_party/libcxx/src/barrier.cpp +FILE: ../../../third_party/libcxx/src/bind.cpp +FILE: ../../../third_party/libcxx/src/charconv.cpp +FILE: ../../../third_party/libcxx/src/chrono.cpp +FILE: ../../../third_party/libcxx/src/condition_variable.cpp +FILE: ../../../third_party/libcxx/src/condition_variable_destructor.cpp +FILE: ../../../third_party/libcxx/src/debug.cpp +FILE: ../../../third_party/libcxx/src/exception.cpp +FILE: ../../../third_party/libcxx/src/experimental/memory_resource.cpp +FILE: ../../../third_party/libcxx/src/filesystem/directory_iterator.cpp +FILE: ../../../third_party/libcxx/src/filesystem/filesystem_common.h +FILE: ../../../third_party/libcxx/src/filesystem/int128_builtins.cpp +FILE: ../../../third_party/libcxx/src/filesystem/operations.cpp +FILE: ../../../third_party/libcxx/src/filesystem/posix_compat.h +FILE: ../../../third_party/libcxx/src/format.cpp +FILE: ../../../third_party/libcxx/src/functional.cpp +FILE: ../../../third_party/libcxx/src/future.cpp +FILE: ../../../third_party/libcxx/src/hash.cpp +FILE: ../../../third_party/libcxx/src/include/apple_availability.h +FILE: ../../../third_party/libcxx/src/include/atomic_support.h +FILE: ../../../third_party/libcxx/src/include/config_elast.h +FILE: ../../../third_party/libcxx/src/include/refstring.h +FILE: ../../../third_party/libcxx/src/include/ryu/common.h +FILE: ../../../third_party/libcxx/src/include/ryu/d2fixed.h +FILE: ../../../third_party/libcxx/src/include/ryu/d2fixed_full_table.h +FILE: ../../../third_party/libcxx/src/include/ryu/d2s.h +FILE: ../../../third_party/libcxx/src/include/ryu/d2s_full_table.h +FILE: ../../../third_party/libcxx/src/include/ryu/d2s_intrinsics.h +FILE: ../../../third_party/libcxx/src/include/ryu/digit_table.h +FILE: ../../../third_party/libcxx/src/include/ryu/f2s.h +FILE: ../../../third_party/libcxx/src/include/ryu/ryu.h +FILE: ../../../third_party/libcxx/src/include/sso_allocator.h +FILE: ../../../third_party/libcxx/src/include/to_chars_floating_point.h +FILE: ../../../third_party/libcxx/src/ios.cpp +FILE: ../../../third_party/libcxx/src/ios.instantiations.cpp +FILE: ../../../third_party/libcxx/src/iostream.cpp +FILE: ../../../third_party/libcxx/src/legacy_debug_handler.cpp +FILE: ../../../third_party/libcxx/src/legacy_pointer_safety.cpp +FILE: ../../../third_party/libcxx/src/locale.cpp +FILE: ../../../third_party/libcxx/src/memory.cpp +FILE: ../../../third_party/libcxx/src/mutex.cpp +FILE: ../../../third_party/libcxx/src/mutex_destructor.cpp +FILE: ../../../third_party/libcxx/src/new.cpp +FILE: ../../../third_party/libcxx/src/optional.cpp +FILE: ../../../third_party/libcxx/src/random.cpp +FILE: ../../../third_party/libcxx/src/random_shuffle.cpp +FILE: ../../../third_party/libcxx/src/regex.cpp +FILE: ../../../third_party/libcxx/src/ryu/d2fixed.cpp +FILE: ../../../third_party/libcxx/src/ryu/d2s.cpp +FILE: ../../../third_party/libcxx/src/ryu/f2s.cpp +FILE: ../../../third_party/libcxx/src/shared_mutex.cpp +FILE: ../../../third_party/libcxx/src/stdexcept.cpp +FILE: ../../../third_party/libcxx/src/string.cpp +FILE: ../../../third_party/libcxx/src/strstream.cpp +FILE: ../../../third_party/libcxx/src/support/ibm/mbsnrtowcs.cpp +FILE: ../../../third_party/libcxx/src/support/ibm/wcsnrtombs.cpp +FILE: ../../../third_party/libcxx/src/support/ibm/xlocale_zos.cpp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_fallback.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_glibcxx.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_libcxxabi.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_libcxxrt.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_msvc.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_cxxabi.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_glibcxx.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_msvc.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_unimplemented.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/new_handler_fallback.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/stdexcept_default.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/stdexcept_vcruntime.ipp +FILE: ../../../third_party/libcxx/src/support/win32/locale_win32.cpp +FILE: ../../../third_party/libcxx/src/support/win32/support.cpp +FILE: ../../../third_party/libcxx/src/support/win32/thread_win32.cpp +FILE: ../../../third_party/libcxx/src/system_error.cpp +FILE: ../../../third_party/libcxx/src/thread.cpp +FILE: ../../../third_party/libcxx/src/typeinfo.cpp +FILE: ../../../third_party/libcxx/src/utility.cpp +FILE: ../../../third_party/libcxx/src/valarray.cpp +FILE: ../../../third_party/libcxx/src/variant.cpp +FILE: ../../../third_party/libcxx/src/vector.cpp +FILE: ../../../third_party/libcxxabi/include/__cxxabi_config.h +FILE: ../../../third_party/libcxxabi/include/cxxabi.h +FILE: ../../../third_party/libcxxabi/src/abort_message.cpp +FILE: ../../../third_party/libcxxabi/src/abort_message.h +FILE: ../../../third_party/libcxxabi/src/aix_state_tab_eh.inc +FILE: ../../../third_party/libcxxabi/src/cxa_aux_runtime.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_default_handlers.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_demangle.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_exception.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_exception.h +FILE: ../../../third_party/libcxxabi/src/cxa_exception_storage.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_guard.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_guard_impl.h +FILE: ../../../third_party/libcxxabi/src/cxa_handlers.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_handlers.h +FILE: ../../../third_party/libcxxabi/src/cxa_noexception.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_personality.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_thread_atexit.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_vector.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_virtual.cpp +FILE: ../../../third_party/libcxxabi/src/demangle/DemangleConfig.h +FILE: ../../../third_party/libcxxabi/src/demangle/ItaniumDemangle.h +FILE: ../../../third_party/libcxxabi/src/demangle/ItaniumNodes.def +FILE: ../../../third_party/libcxxabi/src/demangle/StringView.h +FILE: ../../../third_party/libcxxabi/src/demangle/Utility.h +FILE: ../../../third_party/libcxxabi/src/fallback_malloc.cpp +FILE: ../../../third_party/libcxxabi/src/fallback_malloc.h +FILE: ../../../third_party/libcxxabi/src/private_typeinfo.cpp +FILE: ../../../third_party/libcxxabi/src/private_typeinfo.h +FILE: ../../../third_party/libcxxabi/src/stdlib_exception.cpp +FILE: ../../../third_party/libcxxabi/src/stdlib_new_delete.cpp +FILE: ../../../third_party/libcxxabi/src/stdlib_stdexcept.cpp +FILE: ../../../third_party/libcxxabi/src/stdlib_typeinfo.cpp +---------------------------------------------------------------------------------------------------- +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. +==================================================================================================== + +==================================================================================================== +LIBRARY: zlib +ORIGIN: ../../../third_party/zlib/zlib.h +TYPE: LicenseType.zlib +FILE: ../../../third_party/zlib/adler32.c +FILE: ../../../third_party/zlib/compress.c +FILE: ../../../third_party/zlib/contrib/optimizations/inffast_chunk.c +FILE: ../../../third_party/zlib/contrib/optimizations/inffast_chunk.h +FILE: ../../../third_party/zlib/contrib/optimizations/inflate.c +FILE: ../../../third_party/zlib/crc32.c +FILE: ../../../third_party/zlib/crc_folding.c +FILE: ../../../third_party/zlib/deflate.c +FILE: ../../../third_party/zlib/deflate.h +FILE: ../../../third_party/zlib/gzclose.c +FILE: ../../../third_party/zlib/gzguts.h +FILE: ../../../third_party/zlib/gzlib.c +FILE: ../../../third_party/zlib/gzread.c +FILE: ../../../third_party/zlib/gzwrite.c +FILE: ../../../third_party/zlib/infback.c +FILE: ../../../third_party/zlib/inffast.c +FILE: ../../../third_party/zlib/inffast.h +FILE: ../../../third_party/zlib/inflate.c +FILE: ../../../third_party/zlib/inflate.h +FILE: ../../../third_party/zlib/inftrees.c +FILE: ../../../third_party/zlib/inftrees.h +FILE: ../../../third_party/zlib/trees.c +FILE: ../../../third_party/zlib/uncompr.c +FILE: ../../../third_party/zlib/zconf.h +FILE: ../../../third_party/zlib/zconf.h.cmakein +FILE: ../../../third_party/zlib/zlib.h +FILE: ../../../third_party/zlib/zutil.c +FILE: ../../../third_party/zlib/zutil.h +---------------------------------------------------------------------------------------------------- +Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: zlib +ORIGIN: ../../../third_party/zlib/contrib/minizip/crypt.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/zlib/contrib/minizip/crypt.h +---------------------------------------------------------------------------------------------------- +Copyright (C) 1998-2005 Gilles Vollant +==================================================================================================== + +==================================================================================================== +LIBRARY: zlib +ORIGIN: ../../../third_party/zlib/contrib/optimizations/chunkcopy.h + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/zlib/contrib/optimizations/chunkcopy.h +---------------------------------------------------------------------------------------------------- +Copyright (C) 2017 ARM, Inc. +Copyright 2017 The Chromium Authors + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +==================================================================================================== + +==================================================================================================== +LIBRARY: libcxx +LIBRARY: libcxxabi +ORIGIN: ../../../third_party/libcxx/LICENSE.TXT +ORIGIN: ../../../third_party/libcxxabi/LICENSE.TXT +TYPE: LicenseType.mit +FILE: ../../../third_party/libcxx/include/module.modulemap.in +FILE: ../../../third_party/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.nodebug.noincomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.nodebug.noincomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.debug.noincomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.nodebug.incomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.debug.incomplete.abilist +FILE: ../../../third_party/libcxx/lib/libc++abi.exp +FILE: ../../../third_party/libcxx/lib/libc++unexp.exp +FILE: ../../../third_party/libcxx/lib/notweak.exp +FILE: ../../../third_party/libcxx/lib/weak.exp +FILE: ../../../third_party/libcxx/src/chrono_system_time_init.h +FILE: ../../../third_party/libcxx/src/experimental/memory_resource_init_helper.h +FILE: ../../../third_party/libcxx/src/iostream_init.h +FILE: ../../../third_party/libcxxabi/fuzz/cxa_demangle_fuzzer.cpp +FILE: ../../../third_party/libcxxabi/lib/exceptions.exp +FILE: ../../../third_party/libcxxabi/lib/itanium-base.exp +FILE: ../../../third_party/libcxxabi/lib/new-delete.exp +FILE: ../../../third_party/libcxxabi/lib/personality-sjlj.exp +FILE: ../../../third_party/libcxxabi/lib/personality-v0.exp +---------------------------------------------------------------------------------------------------- +Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: libcxx +LIBRARY: libcxxabi +ORIGIN: ../../../third_party/libcxx/LICENSE.TXT +ORIGIN: ../../../third_party/libcxxabi/LICENSE.TXT +TYPE: LicenseType.bsd +FILE: ../../../third_party/libcxx/include/module.modulemap.in +FILE: ../../../third_party/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.nodebug.noincomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.nodebug.noincomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.debug.incomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.debug.noincomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.nodebug.incomplete.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.debug.incomplete.abilist +FILE: ../../../third_party/libcxx/lib/libc++abi.exp +FILE: ../../../third_party/libcxx/lib/libc++unexp.exp +FILE: ../../../third_party/libcxx/lib/notweak.exp +FILE: ../../../third_party/libcxx/lib/weak.exp +FILE: ../../../third_party/libcxx/src/chrono_system_time_init.h +FILE: ../../../third_party/libcxx/src/experimental/memory_resource_init_helper.h +FILE: ../../../third_party/libcxx/src/iostream_init.h +FILE: ../../../third_party/libcxxabi/fuzz/cxa_demangle_fuzzer.cpp +FILE: ../../../third_party/libcxxabi/lib/exceptions.exp +FILE: ../../../third_party/libcxxabi/lib/itanium-base.exp +FILE: ../../../third_party/libcxxabi/lib/new-delete.exp +FILE: ../../../third_party/libcxxabi/lib/personality-sjlj.exp +FILE: ../../../third_party/libcxxabi/lib/personality-v0.exp +---------------------------------------------------------------------------------------------------- +Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT + +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: zlib +ORIGIN: ../../../third_party/zlib/adler32_simd.c + ../../../LICENSE +ORIGIN: ../../../third_party/zlib/adler32_simd.h + ../../../LICENSE +ORIGIN: ../../../third_party/zlib/crc32_simd.c + ../../../LICENSE +ORIGIN: ../../../third_party/zlib/crc32_simd.h + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/zlib/adler32_simd.c +FILE: ../../../third_party/zlib/adler32_simd.h +FILE: ../../../third_party/zlib/crc32_simd.c +FILE: ../../../third_party/zlib/crc32_simd.h +---------------------------------------------------------------------------------------------------- +Copyright 2017 The Chromium Authors + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +==================================================================================================== + +==================================================================================================== +LIBRARY: zlib +ORIGIN: ../../../third_party/zlib/contrib/bench/zlib_bench.cc + ../../../LICENSE +ORIGIN: ../../../third_party/zlib/cpu_features.c + ../../../LICENSE +ORIGIN: ../../../third_party/zlib/cpu_features.h + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/zlib/contrib/bench/zlib_bench.cc +FILE: ../../../third_party/zlib/cpu_features.c +FILE: ../../../third_party/zlib/cpu_features.h +---------------------------------------------------------------------------------------------------- +Copyright 2018 The Chromium Authors + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +==================================================================================================== + +==================================================================================================== +LIBRARY: libcxx +ORIGIN: ../../../third_party/libcxx/src/include/ryu/common.h +ORIGIN: ../../../third_party/libcxx/src/include/ryu/d2fixed.h +ORIGIN: ../../../third_party/libcxx/src/include/ryu/d2fixed_full_table.h +ORIGIN: ../../../third_party/libcxx/src/include/ryu/d2s.h +ORIGIN: ../../../third_party/libcxx/src/include/ryu/d2s_full_table.h +ORIGIN: ../../../third_party/libcxx/src/include/ryu/d2s_intrinsics.h +ORIGIN: ../../../third_party/libcxx/src/include/ryu/digit_table.h +ORIGIN: ../../../third_party/libcxx/src/include/ryu/f2s.h +ORIGIN: ../../../third_party/libcxx/src/include/ryu/ryu.h +ORIGIN: ../../../third_party/libcxx/src/ryu/d2fixed.cpp +ORIGIN: ../../../third_party/libcxx/src/ryu/d2s.cpp +ORIGIN: ../../../third_party/libcxx/src/ryu/f2s.cpp +TYPE: LicenseType.unknown +FILE: ../../../third_party/libcxx/src/include/ryu/common.h +FILE: ../../../third_party/libcxx/src/include/ryu/d2fixed.h +FILE: ../../../third_party/libcxx/src/include/ryu/d2fixed_full_table.h +FILE: ../../../third_party/libcxx/src/include/ryu/d2s.h +FILE: ../../../third_party/libcxx/src/include/ryu/d2s_full_table.h +FILE: ../../../third_party/libcxx/src/include/ryu/d2s_intrinsics.h +FILE: ../../../third_party/libcxx/src/include/ryu/digit_table.h +FILE: ../../../third_party/libcxx/src/include/ryu/f2s.h +FILE: ../../../third_party/libcxx/src/include/ryu/ryu.h +FILE: ../../../third_party/libcxx/src/ryu/d2fixed.cpp +FILE: ../../../third_party/libcxx/src/ryu/d2s.cpp +FILE: ../../../third_party/libcxx/src/ryu/f2s.cpp +---------------------------------------------------------------------------------------------------- +Copyright 2018 Ulf Adams +Copyright (c) Microsoft Corporation. All rights reserved. + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: zlib +ORIGIN: ../../../third_party/zlib/contrib/optimizations/insert_string.h + ../../../LICENSE +ORIGIN: ../../../third_party/zlib/google/compression_utils_portable.cc + ../../../LICENSE +ORIGIN: ../../../third_party/zlib/google/compression_utils_portable.h + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/zlib/contrib/optimizations/insert_string.h +FILE: ../../../third_party/zlib/google/compression_utils_portable.cc +FILE: ../../../third_party/zlib/google/compression_utils_portable.h +---------------------------------------------------------------------------------------------------- +Copyright 2019 The Chromium Authors + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +==================================================================================================== + +==================================================================================================== +LIBRARY: zlib +ORIGIN: ../../../third_party/zlib/slide_hash_simd.h + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/zlib/slide_hash_simd.h +---------------------------------------------------------------------------------------------------- +Copyright 2022 The Chromium Authors + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +==================================================================================================== + +==================================================================================================== +LIBRARY: zlib +ORIGIN: ../../../third_party/zlib/contrib/minizip/unzip.h +ORIGIN: ../../../third_party/zlib/contrib/minizip/zip.h +TYPE: LicenseType.zlib +FILE: ../../../third_party/zlib/contrib/minizip/unzip.h +FILE: ../../../third_party/zlib/contrib/minizip/zip.h +---------------------------------------------------------------------------------------------------- +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: zlib +ORIGIN: ../../../third_party/zlib/LICENSE +TYPE: LicenseType.zlib +FILE: ../../../third_party/zlib/chromeconf.h +FILE: ../../../third_party/zlib/contrib/minizip/ChangeLogUnzip +FILE: ../../../third_party/zlib/contrib/minizip/ioapi.c +FILE: ../../../third_party/zlib/contrib/minizip/ioapi.h +FILE: ../../../third_party/zlib/contrib/minizip/iowin32.c +FILE: ../../../third_party/zlib/contrib/minizip/iowin32.h +FILE: ../../../third_party/zlib/contrib/minizip/mztools.c +FILE: ../../../third_party/zlib/contrib/minizip/mztools.h +FILE: ../../../third_party/zlib/contrib/minizip/unzip.c +FILE: ../../../third_party/zlib/contrib/minizip/unzip.h +FILE: ../../../third_party/zlib/contrib/minizip/zip.c +FILE: ../../../third_party/zlib/contrib/minizip/zip.h +FILE: ../../../third_party/zlib/crc32.h +FILE: ../../../third_party/zlib/google/compression_utils.cc +FILE: ../../../third_party/zlib/google/compression_utils.h +FILE: ../../../third_party/zlib/google/redact.h +FILE: ../../../third_party/zlib/google/test_data.filelist +FILE: ../../../third_party/zlib/google/test_data.globlist +FILE: ../../../third_party/zlib/google/zip.cc +FILE: ../../../third_party/zlib/google/zip.h +FILE: ../../../third_party/zlib/google/zip_internal.cc +FILE: ../../../third_party/zlib/google/zip_internal.h +FILE: ../../../third_party/zlib/google/zip_reader.cc +FILE: ../../../third_party/zlib/google/zip_reader.h +FILE: ../../../third_party/zlib/google/zip_writer.cc +FILE: ../../../third_party/zlib/google/zip_writer.h +FILE: ../../../third_party/zlib/inffixed.h +FILE: ../../../third_party/zlib/patches/0000-build.patch +FILE: ../../../third_party/zlib/patches/0001-simd.patch +FILE: ../../../third_party/zlib/patches/0002-uninitializedcheck.patch +FILE: ../../../third_party/zlib/patches/0003-uninitializedjump.patch +FILE: ../../../third_party/zlib/patches/0004-fix-uwp.patch +FILE: ../../../third_party/zlib/patches/0005-infcover-gtest.patch +FILE: ../../../third_party/zlib/patches/0006-fix-check_match.patch +FILE: ../../../third_party/zlib/patches/0007-zero-init-deflate-window.patch +FILE: ../../../third_party/zlib/patches/0008-minizip-zip-unzip-tools.patch +FILE: ../../../third_party/zlib/patches/0009-infcover-oob.patch +FILE: ../../../third_party/zlib/patches/0010-cmake-enable-simd.patch +FILE: ../../../third_party/zlib/patches/0011-avx512.patch +FILE: ../../../third_party/zlib/trees.h +FILE: ../../../third_party/zlib/zlib.map +FILE: ../../../third_party/zlib/zlib.pc.cmakein +---------------------------------------------------------------------------------------------------- +version 1.2.12, March 27th, 2022 + +Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +==================================================================================================== + +Total license count: 13 diff --git a/common/config.gni b/common/config.gni index ef1b0288bb844ace07cc4cc355c40b49cb6b11b3..effded6c771b8d0f84a9d95b8cba822cd4cfb4a7 100644 --- a/common/config.gni +++ b/common/config.gni @@ -6,6 +6,10 @@ if (is_android) { import("//build/config/android/config.gni") } +if (is_ohos) { + import("//build/config/ohos/config.gni") +} + if (target_cpu == "arm" || target_cpu == "arm64") { import("//build/config/arm.gni") } @@ -149,4 +153,4 @@ if (flutter_prebuilt_dart_sdk) { build_engine_artifacts = flutter_build_engine_artifacts && (current_toolchain == host_toolchain || - (is_linux && !is_chromeos && current_cpu != "arm") || is_mac || is_win) + (is_linux && !is_chromeos && current_cpu != "arm") || is_mac || is_win || is_ohos) diff --git a/common/graphics/persistent_cache.cc b/common/graphics/persistent_cache.cc index 0d24125d1f00b9f14417749f7a1c47f1ef9479c8..be8b573fbc9e7de579bfa8e7e8c7d9ee59fd865e 100644 --- a/common/graphics/persistent_cache.cc +++ b/common/graphics/persistent_cache.cc @@ -250,7 +250,7 @@ std::vector PersistentCache::LoadSkSLs() const { mapping = asset_manager_->GetAsMapping(kAssetFileName); } if (mapping == nullptr) { - FML_LOG(INFO) << "No sksl asset found."; + FML_LOG(ERROR) << "No sksl asset found."; } else { FML_LOG(INFO) << "Found sksl asset. Loading SkSLs from it..."; rapidjson::Document json_doc; diff --git a/common/settings.h b/common/settings.h index 0145f705b6aa151cb93a1a46f31bb6a0cc70fb36..d6a6946d655b314a593e03b2a8620b8639ec3f78 100644 --- a/common/settings.h +++ b/common/settings.h @@ -30,6 +30,13 @@ enum class AndroidRenderingAPI { kSkiaOpenGLES }; +// The combination of targeted graphics API and Impeller support. +enum class OHOSRenderingAPI { + kSoftware, + kOpenGLES, + kImpellerVulkan, +}; + class FrameTiming { public: enum Phase { @@ -236,6 +243,9 @@ struct Settings { AndroidRenderingAPI android_rendering_api = AndroidRenderingAPI::kSkiaOpenGLES; + // The selected Android rendering API. + OHOSRenderingAPI ohos_rendering_api = OHOSRenderingAPI::kOpenGLES; + // Requests a specific rendering backend. std::optional requested_rendering_backend; diff --git a/display_list/testing/BUILD.gn b/display_list/testing/BUILD.gn index f94799b9729afa8c5ed433eb11d9aa069ed19433..a438c22d45e2b43dcccea6072fab393b08d3ec8d 100644 --- a/display_list/testing/BUILD.gn +++ b/display_list/testing/BUILD.gn @@ -23,14 +23,15 @@ source_set("display_list_testing") { public_deps = [ "//flutter/display_list:display_list" ] } -surface_provider_include_software = !is_android && !is_ios +surface_provider_include_software = !is_android && !is_ios && !is_ohos # iOS and Fuchsia and Windows don't support OpenGL # Note: Windows would need some work to support OpenGL # But, since benchmarks do not run on Windows and rendertests only # runs on SW by default, this restriction currently only limits the # ability to manually cross-check OpenGL on Windows for rendertests -surface_provider_include_gl = !is_fuchsia && !is_ios && !is_win && !is_mac +surface_provider_include_gl = + !is_fuchsia && !is_ios && !is_win && !is_mac && !is_ohos # TODO (https://github.com/flutter/flutter/issues/107357): # impeller_enable_vulkan currently requires skia to not use VMA, which in turn diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc index 94339690fca92bed22b760a3d3bbb7ccb51c1d94..7f07aacd44bbd543242bba5d2f923eea384106c6 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -1058,6 +1058,7 @@ TEST(RasterCacheUtilsTest, SkM44IntegralTransCTM) { snaps = false; break; default: + snaps = false; FML_UNREACHABLE(); } auto label = std::to_string(r) + ", " + std::to_string(c); diff --git a/fml/BUILD.gn b/fml/BUILD.gn index d2d4a165c76862bb3d16ac6ac434f4595db51077..a233c46c2e3d15b41855af8e470138e39190bbbf 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -204,7 +204,28 @@ source_set("fml") { ] } - if (is_linux) { + if (is_ohos) { + sources += [ + "platform/ohos/hisysevent_c.cc", + "platform/ohos/hisysevent_c.h", + "platform/ohos/message_loop_ohos.cc", + "platform/ohos/message_loop_ohos.h", + "platform/ohos/napi_util.cc", + + #"platform/ohos/message_loop_ohos_test.cc", + #"platform/ohos/message_loop_ohos_test.h", + "platform/ohos/napi_util.h", + "platform/ohos/ohos_trace_event.cc", + "platform/ohos/paths_ohos.cc", + "platform/ohos/paths_ohos.h", + "platform/ohos/timerfd.cc", + "platform/ohos/timerfd.h", + ] + libs += [ "hilog_ndk.z" ] + libs += [ "ace_napi.z" ] + libs += [ "uv" ] + libs += [ "hitrace_ndk.z" ] + } else if (is_linux) { sources += [ "platform/linux/message_loop_linux.cc", "platform/linux/message_loop_linux.h", diff --git a/fml/build_config.h b/fml/build_config.h index d42a88e5bbce08c8104f91bdf4e1c360b4f0f558..f1481b22c322953b7d46b581088134b99a15779e 100644 --- a/fml/build_config.h +++ b/fml/build_config.h @@ -29,9 +29,20 @@ #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE #define FML_OS_IOS 1 #endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE + #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR #define FML_OS_IOS_SIMULATOR 1 #endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE + +#elif defined(__OHOS_FAMILY__) +#define FML_OS_OHOS 1 +// include a system header to pull in features.h for glibc/uclibc macros. +#include +#if defined(__GLIBC__) && !defined(__UCLIBC__) +// we really are using glibc, not uClibc pretending to be glibc +#define LIBC_GLIBC 1 +#endif + #elif defined(__linux__) #define FML_OS_LINUX 1 // include a system header to pull in features.h for glibc/uclibc macros. @@ -40,6 +51,8 @@ // we really are using glibc, not uClibc pretending to be glibc #define LIBC_GLIBC 1 #endif + + #elif defined(_WIN32) #define FML_OS_WIN 1 #elif defined(__FreeBSD__) diff --git a/fml/log_level.h b/fml/log_level.h index a53b4ab44cde27a593eab1b82eb4a51e98a7cc70..54e29023c7425f6c74267d98d455499f8393439f 100644 --- a/fml/log_level.h +++ b/fml/log_level.h @@ -44,6 +44,9 @@ constexpr LogSeverity LOG_IMPORTANT = kLogImportant; // NOLINTNEXTLINE(readability-identifier-naming) constexpr LogSeverity LOG_FATAL = kLogFatal; +constexpr LogSeverity LOG_NUM_SEVERITIES = kLogNumSeverities; +constexpr LogSeverity LOG_DEBUG = 0; + // One of the Windows headers defines ERROR to 0. This makes the token // concatenation in FML_LOG(ERROR) to resolve to LOG_0. We define this back to // the appropriate log level. diff --git a/fml/logging.cc b/fml/logging.cc index 15bfa9de368b4ec3b6f22a9c1326a0665e0ddcef..7672eb13c55759772eacc1bf911e75670ac770a8 100644 --- a/fml/logging.cc +++ b/fml/logging.cc @@ -22,6 +22,45 @@ #include "flutter/fml/platform/fuchsia/log_state.h" #endif +#ifdef FML_OS_OHOS +#include + +extern "C" { +#define OHOS_LOG_TYPE_APP 0 +#define HILOG_LOG_DOMAIN 0 +#define HILOG_LOG_TAG "XComFlutterEngine" +typedef enum { + /** Debug level to be used by {@link OH_LOG_DEBUG} */ + HILOG_LOG_DEBUG = 3, + /** Informational level to be used by {@link OH_LOG_INFO} */ + HILOG_LOG_INFO = 4, + /** Warning level to be used by {@link OH_LOG_WARN} */ + HILOG_LOG_WARN = 5, + /** Error level to be used by {@link OH_LOG_ERROR} */ + HILOG_LOG_ERROR = 6, + /** Fatal level to be used by {@link OH_LOG_FATAL} */ + HILOG_LOG_FATAL = 7, +} HiLog_LogLevel; +int OH_LOG_Print(int type, + HiLog_LogLevel level, + unsigned int domain, + const char* tag, + const char* fmt, + ...); +#define HILOG_LOG(level, ...) \ + ((void)OH_LOG_Print(OHOS_LOG_TYPE_APP, (level), HILOG_LOG_DOMAIN, \ + HILOG_LOG_TAG, __VA_ARGS__)) +#define HILOG_DEBUG(...) \ + ((void)OH_LOG_Print(OHOS_LOG_TYPE_APP, HILOG_LOG_DEBUG, HILOG_LOG_DOMAIN, \ + HILOG_LOG_TAG, __VA_ARGS__)) +#define HILOG_ERROR(...) \ + ((void)OH_LOG_Print(OHOS_LOG_TYPE_APP, HILOG_LOG_ERROR, HILOG_LOG_DOMAIN, \ + HILOG_LOG_TAG, __VA_ARGS__)) +#define HILOG_INFO(...) \ + ((void)OH_LOG_Print(OHOS_LOG_TYPE_APP, HILOG_LOG_INFO, HILOG_LOG_DOMAIN, \ + HILOG_LOG_TAG, __VA_ARGS__)) +} +#endif namespace fml { namespace { @@ -197,6 +236,31 @@ LogMessage::~LogMessage() { } } buffer.FlushRecord(); +#elif defined(FML_OS_OHOS) + HiLog_LogLevel fx_severity; + + switch (severity_) { + case LOG_INFO: + fx_severity = HILOG_LOG_INFO; + break; + case LOG_WARNING: + fx_severity = HILOG_LOG_WARN; + break; + case LOG_ERROR: + fx_severity = HILOG_LOG_ERROR; + break; + case LOG_FATAL: + fx_severity = HILOG_LOG_FATAL; + break; + default: + fx_severity = HILOG_LOG_INFO; + } // end switch + HILOG_LOG(fx_severity, "Thread:%{public}lu %{public}s", pthread_self(), + stream_.str().c_str()); + + std::cerr << stream_.str(); + std::cerr.flush(); + #else // Don't use std::cerr here, because it may not be initialized properly yet. fprintf(stderr, "%s", stream_.str().c_str()); @@ -218,7 +282,11 @@ bool ShouldCreateLogMessage(LogSeverity severity) { } void KillProcess() { +#ifdef FML_OS_OHOS + HILOG_ERROR("FML KILL PROCESS"); +#else abort(); +#endif } } // namespace fml diff --git a/fml/logging.h b/fml/logging.h index 146c35bc88e28d71435e4a53a9ba71926769f955..51d249bcad5459c14e7ddaa0e1b3fd0dde173a58 100644 --- a/fml/logging.h +++ b/fml/logging.h @@ -60,7 +60,7 @@ int GetVlogVerbosity(); // kLogFatal and above is always true. bool ShouldCreateLogMessage(LogSeverity severity); -[[noreturn]] void KillProcess(); +void KillProcess(); } // namespace fml diff --git a/fml/logging_unittests.cc b/fml/logging_unittests.cc index fd0eca048e9ac48b8388562130acdb2d07db8b74..e8772a2990d7cfab38195f04aa778ea00b0df8cf 100644 --- a/fml/logging_unittests.cc +++ b/fml/logging_unittests.cc @@ -55,11 +55,13 @@ static MakeSureFmlLogDoesNotSegfaultWhenStaticallyCalled fml_log_static_check_; int UnreachableScopeWithoutReturnDoesNotMakeCompilerMad() { KillProcess(); // return 0; <--- Missing but compiler is fine. + return 0; } int UnreachableScopeWithMacroWithoutReturnDoesNotMakeCompilerMad() { FML_UNREACHABLE(); // return 0; <--- Missing but compiler is fine. + return 0; } TEST(LoggingTest, UnreachableKillProcess) { diff --git a/fml/message_loop.cc b/fml/message_loop.cc index f392265583563537c8b3bdf2352d2c3bbe78d1a0..a4a90dab6449018ce8cdeacf182cf608ca7766a2 100644 --- a/fml/message_loop.cc +++ b/fml/message_loop.cc @@ -29,15 +29,23 @@ void MessageLoop::EnsureInitializedForCurrentThread() { // Already initialized. return; } - tls_message_loop.reset(new MessageLoop()); + tls_message_loop.reset(new MessageLoop(nullptr)); +} + +void MessageLoop::EnsureInitializedForCurrentThread(void* platform_loop) { + if (tls_message_loop.get() != nullptr) { + // Already initialized. + return; + } + tls_message_loop.reset(new MessageLoop(platform_loop)); } bool MessageLoop::IsInitializedForCurrentThread() { return tls_message_loop.get() != nullptr; } -MessageLoop::MessageLoop() - : loop_(MessageLoopImpl::Create()), +MessageLoop::MessageLoop(void* platform_loop) + : loop_(MessageLoopImpl::Create(platform_loop)), task_runner_(fml::MakeRefCounted(loop_)) { FML_CHECK(loop_); FML_CHECK(task_runner_); diff --git a/fml/message_loop.h b/fml/message_loop.h index ccc1f70b93b8df4ffe4cb3a382fd9530627e80ae..b33449a4610922c5e1e46ac6cbd54348d6dc6710 100644 --- a/fml/message_loop.h +++ b/fml/message_loop.h @@ -46,6 +46,8 @@ class MessageLoop { static void EnsureInitializedForCurrentThread(); + static void EnsureInitializedForCurrentThread(void* platform_loop); + /// Returns true if \p EnsureInitializedForCurrentThread has been called on /// this thread already. static bool IsInitializedForCurrentThread(); @@ -64,7 +66,7 @@ class MessageLoop { fml::RefPtr loop_; fml::RefPtr task_runner_; - MessageLoop(); + MessageLoop(void* platform_loop); fml::RefPtr GetLoopImpl() const; diff --git a/fml/message_loop_impl.cc b/fml/message_loop_impl.cc index 01f4d58d769f487f6326649b97d7c4b829bfdf21..8562284afbc7393ea9adb0a5c2dcf5b29d756472 100644 --- a/fml/message_loop_impl.cc +++ b/fml/message_loop_impl.cc @@ -18,6 +18,8 @@ #include "flutter/fml/platform/android/message_loop_android.h" #elif OS_FUCHSIA #include "flutter/fml/platform/fuchsia/message_loop_fuchsia.h" +#elif FML_OS_OHOS +#include "flutter/fml/platform/ohos/message_loop_ohos.h" #elif FML_OS_LINUX #include "flutter/fml/platform/linux/message_loop_linux.h" #elif FML_OS_WIN @@ -26,13 +28,15 @@ namespace fml { -fml::RefPtr MessageLoopImpl::Create() { +fml::RefPtr MessageLoopImpl::Create(void* platform_loop) { #if FML_OS_MACOSX return fml::MakeRefCounted(); #elif FML_OS_ANDROID return fml::MakeRefCounted(); #elif OS_FUCHSIA return fml::MakeRefCounted(); +#elif FML_OS_OHOS +return fml::MakeRefCounted(platform_loop); #elif FML_OS_LINUX return fml::MakeRefCounted(); #elif FML_OS_WIN diff --git a/fml/message_loop_impl.h b/fml/message_loop_impl.h index 270cc3c86a9ade6627e79df23dcb91c502e42bbb..865ffb43ebe3e1667e268faeb830ea5ea0d122ca 100644 --- a/fml/message_loop_impl.h +++ b/fml/message_loop_impl.h @@ -31,7 +31,7 @@ namespace fml { class MessageLoopImpl : public Wakeable, public fml::RefCountedThreadSafe { public: - static fml::RefPtr Create(); + static fml::RefPtr Create(void* platform_loop); virtual ~MessageLoopImpl(); diff --git a/fml/platform/ohos/hisysevent_c.cc b/fml/platform/ohos/hisysevent_c.cc new file mode 100644 index 0000000000000000000000000000000000000000..7e369daa7cdfd71965061124e7821525249da9e8 --- /dev/null +++ b/fml/platform/ohos/hisysevent_c.cc @@ -0,0 +1,113 @@ +#if !defined(_WIN32) && !defined(_WIN64) +#include "flutter/fml/platform/ohos/hisysevent_c.h" + +namespace fml { + +static void* handle = NULL; +static HiSysEvent_Write_Def HiSysEvent_Write = NULL; +static const char* domain_ = "PERFORMANCE"; +static const char* event_ = "INTERACTION_HITCH_TIME_RATIO"; +static const HiSysEventEventType kType = kHisyseventBehavior; +static HiSysEventParam params_[12] = { + { + .name = "SCENE_ID", + .t = kHisyseventString, + .v = { .s = "" }, + .arraySize = 0, + }, + { + .name = "PROCESS_NAME", + .t = kHisyseventString, + .v = { .s = "" }, + .arraySize = 0, + }, + { + .name = "MODULE_NAME", + .t = kHisyseventString, + .v = { .s = "" }, + .arraySize = 0, + }, + { + .name = "ABILITY_NAME", + .t = kHisyseventString, + .v = { .s = "" }, + .arraySize = 0, + }, + { + .name = "PAGE_URL", + .t = kHisyseventString, + .v = { .s = "" }, + .arraySize = 0, + }, + { + .name = "UI_START_TIME", + .t = kHisyseventUint64, + .v = { .ui64 = 0 }, + .arraySize = 0, + }, + { + .name = "RS_START_TIME", + .t = kHisyseventUint64, + .v = { .ui64 = 0 }, + .arraySize = 0, + }, + { + .name = "DURATION", + .t = kHisyseventUint64, + .v = { .ui64 = 0 }, + .arraySize = 0, + }, + { + .name = "HITCH_TIME", + .t = kHisyseventUint64, + .v = { .ui64 = 0 }, + .arraySize = 0, + }, + { + .name = "HITCH_TIME_RATIO", + .t = kHisyseventFloat, + .v = { .f = 0 }, + .arraySize = 0, + }, + { + .name = "IS_FOLD_DISP", + .t = kHisyseventBool, + .v = { .b = false }, + .arraySize = 0, + }, + { + .name = "BUNDLE_NAME_EX", + .t = kHisyseventString, + .v = { .s = "" }, + .arraySize = 0, + }, +}; + +static const size_t kSize = 12; +int HiSysEventWrite(const char* name, uint64_t time) { + if (handle == NULL && HiSysEvent_Write == NULL) { + handle = dlopen("/system/lib64/chipset-pub-sdk/libhisysevent.z.so", RTLD_LAZY); + if (handle != NULL) { + HiSysEvent_Write = reinterpret_cast(dlsym(handle, "HiSysEvent_Write")); + if (HiSysEvent_Write == NULL) { + FML_DLOG(ERROR) << "dlsym HiSysEvent_Write return NULL\n"; + dlclose(handle); + handle = NULL; + } + } else { + FML_DLOG(ERROR) << "dlopen libhisysevent.z.so return NULL\n"; + } + } + + if (HiSysEvent_Write != NULL) { + params_[3].v.s = name; + params_[7].v.ui64 = time; + int ret = HiSysEvent_Write(__FUNCTION__, __LINE__, domain_, event_, kType, params_, kSize); + return ret; + } else { + FML_DLOG(ERROR) << "HiSysEvent_Write is NULL\n"; + return -1; + } +} +} // namespace fml +#endif // #if !defined(_WIN32) && !defined(_WIN64) diff --git a/fml/platform/ohos/hisysevent_c.h b/fml/platform/ohos/hisysevent_c.h new file mode 100644 index 0000000000000000000000000000000000000000..ac05bb01febf1ae101ef69a3dbfd9c9d474ee5ed --- /dev/null +++ b/fml/platform/ohos/hisysevent_c.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * 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 HISYSEVENT_INTERFACES_NATIVE_INNERKITS_HISYSEVENT_INCLUDE_HISYSEVENT_C_H +#define HISYSEVENT_INTERFACES_NATIVE_INNERKITS_HISYSEVENT_INCLUDE_HISYSEVENT_C_H + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#include +#include +#include +#include "flutter/fml/logging.h" + +#if !FLUTTER_RELEASE && defined(FML_OS_OHOS) +#define HISYSEVENT_WRITE_SINGLE(name) \ + ::fml::HiSysEventWrite(name, 0); +#define HISYSEVENT_WRITE_DURATION(name) \ + ::fml::HiSysEventTrace __FML__TOKEN_CAT__2(hisysevent, __LINE__)(name); +#else +#define HISYSEVENT_WRITE_SINGLE(name) +#define HISYSEVENT_WRITE_DURATION(name) +#endif // !FLUTTER_RELEASE && defined(FML_OS_OHOS) + +namespace fml { + +#define MAX_LENGTH_OF_PARAM_NAME 49 + +/** + * @brief Define the type of the param. + */ +enum HiSysEventParamType { + kHisyseventInvalid = 0, + kHisyseventBool = 1, + kHisyseventInt8 = 2, + kHisyseventUint8 = 3, + kHisyseventInt16 = 4, + kHisyseventUint16 = 5, + kHisyseventInt32 = 6, + kHisyseventUint32 = 7, + kHisyseventInt64 = 8, + kHisyseventUint64 = 9, + kHisyseventFloat = 10, + kHisyseventDouble = 11, + kHisyseventString = 12, + kHisyseventBoolArray = 13, + kHisyseventInt8Array = 14, + kHisyseventUint8Array = 15, + kHisyseventInt16Array = 16, + kHisyseventUint16Array = 17, + kHisyseventInt32Array = 18, + kHisyseventUint32Array = 19, + kHisyseventInt64Array = 20, + kHisyseventUint64Array = 21, + kHisyseventFloatArray = 22, + kHisyseventDoubleArray = 23, + kHisyseventStringArray = 24 +}; +typedef enum HiSysEventParamType HiSysEventParamType; + +/** + * @brief Define the value of the param. + */ +union HiSysEventParamValue { + bool b; + int8_t i8; + uint8_t ui8; + int16_t i16; + uint16_t ui16; + int32_t i32; + uint32_t ui32; + int64_t i64; + uint64_t ui64; + float f; + double d; + const char *s; + void *array; +}; +typedef union HiSysEventParamValue HiSysEventParamValue; + +/** + * @brief Define param struct. + */ +struct HiSysEventParam { + char name[MAX_LENGTH_OF_PARAM_NAME]; + HiSysEventParamType t; + HiSysEventParamValue v; + size_t arraySize; +}; +typedef struct HiSysEventParam HiSysEventParam; + +/** + * @brief Event type. + */ +enum HiSysEventEventType { + kHisyseventFault = 1, + kHisyseventStatistic = 2, + kHisyseventSecurity = 3, + kHisyseventBehavior = 4 +}; +typedef enum HiSysEventEventType HiSysEventEventType; + +/** + * @brief Write system event. + * @param domain event domain. + * @param name event name. + * @param type event type. + * @param params event params. + * @param size the size of param list. + * @return 0 means success, less than 0 means failure, greater than 0 means invalid params. + */ +// #define OH_HiSysEvent_Write(domain, name, type, params, size) \ +// HiSysEvent_Write(__FUNCTION__, __LINE__, domain, name, type, params, size) + +// int HiSysEvent_Write(const char* func, int64_t line, const char* domain, const char* name, +// HiSysEventEventType type, const HiSysEventParam params[], size_t size); + +typedef int (*HiSysEvent_Write_Def)(const char* func, int64_t line, const char* domain, const char* name, + HiSysEventEventType type, const HiSysEventParam params[], size_t size); + +int HiSysEventWrite(const char* name, uint64_t time); + +class HiSysEventTrace { +private: + const char* name_; + struct timespec begin_time_; + struct timespec end_time_; + +public: + explicit HiSysEventTrace(const char* name) { + if (name != NULL) { + name_ = name; + } else { + name_ = "flutter default trace name"; + } + clock_gettime(CLOCK_MONOTONIC, &begin_time_); + } + ~HiSysEventTrace() { + clock_gettime(CLOCK_MONOTONIC, &end_time_); + int ret = HiSysEventWrite(name_, (end_time_.tv_sec - begin_time_.tv_sec) * 1e6 + + (end_time_.tv_nsec - begin_time_.tv_nsec) / 1000); // us + FML_DLOG(INFO) << "HiSysEventWrite return " << ret; + } +}; + +} // namespace fml + +#else +#define HISYSEVENT_WRITE_SINGLE(name) +#define HISYSEVENT_WRITE_DURATION(name) +#endif // #if !defined(_WIN32) && !defined(_WIN64) +#endif // HISYSEVENT_INTERFACES_NATIVE_INNERKITS_HISYSEVENT_INCLUDE_HISYSEVENT_C_H diff --git a/fml/platform/ohos/message_loop_ohos.cc b/fml/platform/ohos/message_loop_ohos.cc new file mode 100644 index 0000000000000000000000000000000000000000..0ec2340d2a5080c86d330486868156afa5484c7c --- /dev/null +++ b/fml/platform/ohos/message_loop_ohos.cc @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/fml/platform/ohos/message_loop_ohos.h" +#include +#include "flutter/fml/eintr_wrapper.h" +#include "flutter/fml/logging.h" +#include "flutter/fml/platform/linux/timerfd.h" + +namespace fml { + +static constexpr int kClockType = CLOCK_MONOTONIC; + +void MessageLoopOhos::OnAsyncCallback(uv_async_t* handle) { + reinterpret_cast(handle->data)->OnEventFired(); +} + +void MessageLoopOhos::OnAsyncHandleClose(uv_handle_t* handle) {} + +void MessageLoopOhos::OnPollCallback(uv_poll_t* handle, + int status, + int events) { + if (status < 0) { + FML_LOG(ERROR) << "Poll error:" << uv_strerror(status); + return; + } + + if (events & UV_READABLE) { + reinterpret_cast(handle->data)->OnEventFired(); + } +} + +MessageLoopOhos::MessageLoopOhos(void* platform_loop) + : epoll_fd_(FML_HANDLE_EINTR(::epoll_create(1 /* unused */))), + timer_fd_(::timerfd_create(kClockType, TFD_NONBLOCK | TFD_CLOEXEC)), + running_(false), + is_platform_loop_(false) { + FML_CHECK(epoll_fd_.is_valid()); + FML_CHECK(timer_fd_.is_valid()); + bool added_source = AddOrRemoveTimerSource(true); + FML_CHECK(added_source); + async_handle_.data = this; + if (platform_loop != nullptr) { + is_platform_loop_ = true; + uv_loop_t* loop = reinterpret_cast(platform_loop); + uv_async_init(loop, &async_handle_, + OnAsyncCallback); // ohos after API12 not allow use uv_poll + timerhandle_thread_ = std::thread([this]() { + running_ = true; + TimerFdWatcher(); + }); + } else { + uv_loop_init(&loop_); + uv_poll_init(&loop_, &poll_handle_, timer_fd_.get()); + poll_handle_.data = this; + uv_poll_start(&poll_handle_, UV_READABLE, OnPollCallback); + } +} + +MessageLoopOhos::~MessageLoopOhos() { + if (is_platform_loop_) { + bool removed_source = AddOrRemoveTimerSource(false); + FML_CHECK(removed_source); + } else { + if (uv_loop_alive(&loop_)) { + uv_loop_close(&loop_); + } + } +} + +// |fml::MessageLoopImpl| +void MessageLoopOhos::Run() { + uv_run(&loop_, UV_RUN_DEFAULT); +} + +// |fml::MessageLoopImpl| +void MessageLoopOhos::Terminate() { + running_ = false; + WakeUp(fml::TimePoint::Now()); + if (is_platform_loop_) { + if (timerhandle_thread_.joinable()) { + timerhandle_thread_.join(); + } + uv_close((uv_handle_t*)&async_handle_, OnAsyncHandleClose); + } else { + uv_poll_stop(&poll_handle_); + uv_stop(&loop_); + } +} + +// |fml::MessageLoopImpl| +void MessageLoopOhos::WakeUp(fml::TimePoint time_point) { + bool result = TimerRearm(timer_fd_.get(), time_point); + (void)result; + FML_DCHECK(result); +} + +void MessageLoopOhos::OnEventFired() { + if (TimerDrain(timer_fd_.get())) { + RunExpiredTasksNow(); + } +} + +void MessageLoopOhos::TimerFdWatcher() { + while (running_) { + struct epoll_event event = {}; + + int epoll_result = FML_HANDLE_EINTR( + ::epoll_wait(epoll_fd_.get(), &event, 1, -1 /* timeout */)); + + // Errors are fatal. + if (event.events & (EPOLLERR | EPOLLHUP)) { + running_ = false; + continue; + } + + // Timeouts are fatal since we specified an infinite timeout already. + // Likewise, > 1 is not possible since we waited for one result. + if (epoll_result != 1) { + running_ = false; + continue; + } + + if (event.data.fd == timer_fd_.get()) { + uv_async_send(&async_handle_); + } + } +} + +bool MessageLoopOhos::AddOrRemoveTimerSource(bool add) { + struct epoll_event event = {}; + + event.events = EPOLLIN; + // The data is just for informational purposes so we know when we were worken + // by the FD. + event.data.fd = timer_fd_.get(); + + int ctl_result = + ::epoll_ctl(epoll_fd_.get(), add ? EPOLL_CTL_ADD : EPOLL_CTL_DEL, + timer_fd_.get(), &event); + return ctl_result == 0; +} + +} // namespace fml diff --git a/fml/platform/ohos/message_loop_ohos.h b/fml/platform/ohos/message_loop_ohos.h new file mode 100644 index 0000000000000000000000000000000000000000..3418bb0d157f8791a79ce43fa1a31a054c7a9980 --- /dev/null +++ b/fml/platform/ohos/message_loop_ohos.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_FML_PLATFORM_OHOS_MESSAGE_LOOP_OHOS_H_ +#define FLUTTER_FML_PLATFORM_OHOS_MESSAGE_LOOP_OHOS_H_ + +#include +#include "flutter/fml/macros.h" +#include "flutter/fml/message_loop_impl.h" +#include "flutter/fml/unique_fd.h" +#include +#include + +namespace fml { + +class MessageLoopOhos : public MessageLoopImpl { + private: + uv_async_t async_handle_; + uv_poll_t poll_handle_; + uv_loop_t loop_; + fml::UniqueFD epoll_fd_; + fml::UniqueFD timer_fd_; + std::atomic running_; + std::thread timerhandle_thread_; + bool is_platform_loop_; + + explicit MessageLoopOhos(void* platform_loop); + + ~MessageLoopOhos() override; + + // |fml::MessageLoopImpl| + void Run() override; + + // |fml::MessageLoopImpl| + void Terminate() override; + + // |fml::MessageLoopImpl| + void WakeUp(fml::TimePoint time_point) override; + + void OnEventFired(); + + void TimerFdWatcher(); + + bool AddOrRemoveTimerSource(bool add); + + FML_FRIEND_MAKE_REF_COUNTED(MessageLoopOhos); + FML_FRIEND_REF_COUNTED_THREAD_SAFE(MessageLoopOhos); + FML_DISALLOW_COPY_AND_ASSIGN(MessageLoopOhos); + + public: + static void OnAsyncCallback(uv_async_t* handle); + static void OnAsyncHandleClose(uv_handle_t* handle); + static void OnPollCallback(uv_poll_t* handle, int status, int events); +}; + +} // namespace fml + +#endif // FLUTTER_FML_PLATFORM_OHOS_MESSAGE_LOOP_OHOS_H_ diff --git a/fml/platform/ohos/message_loop_ohos_test.cc b/fml/platform/ohos/message_loop_ohos_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..1aa1db255e040ed052207884bf55782ec751987a --- /dev/null +++ b/fml/platform/ohos/message_loop_ohos_test.cc @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/fml/message_loop.h" + +#include +#include + +#include "flutter/fml/build_config.h" +#include "flutter/fml/concurrent_message_loop.h" +#include "flutter/fml/synchronization/count_down_latch.h" +#include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/fml/task_runner.h" +#include "flutter/fml/time/chrono_timestamp_provider.h" +#include "gtest/gtest.h" + +namespace fml { +namespace message_loop_test { + +#define PLATFORM_SPECIFIC_CAPTURE(...) [__VA_ARGS__] + +/** + * breif: 注册异步任务 + * + */ +void MessageLoopTestPostTask(void) { + bool started = false; + bool terminated = false; + std::thread thread([&started, &terminated]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + auto& loop = fml::MessageLoop::GetCurrent(); + ASSERT_TRUE(loop.GetTaskRunner()); + loop.GetTaskRunner()->PostTask([&terminated]() { + std::cout << "MessageLoopTestPostTask running ..." << std::endl; + fml::MessageLoop::GetCurrent().Terminate(); + terminated = true; + }); + loop.Run(); + started = true; + }); + thread.join(); + ASSERT_TRUE(started); + ASSERT_TRUE(terminated); +} + +/** + * breif: + * 进行观察者测试,注册25个任务后,添加观察者,在回调中增加观察者回调打印 + * + */ +void MessageLoopTestObserverFire(void) { + bool started = false; + bool terminated = false; + std::thread thread([&started, &terminated]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + const size_t count = 25; + auto& loop = fml::MessageLoop::GetCurrent(); + size_t task_count = 0; + size_t obs_count = 0; + auto obs = PLATFORM_SPECIFIC_CAPTURE(&obs_count)() { + obs_count++; + std::cout << "MessageLoopTestObserverFire obs_count" << obs_count + << std::endl; + }; + for (size_t i = 0; i < count; i++) { + loop.GetTaskRunner()->PostTask( + PLATFORM_SPECIFIC_CAPTURE(&terminated, i, &task_count)() { + ASSERT_EQ(task_count, i); + task_count++; + if (count == i + 1) { + fml::MessageLoop::GetCurrent().Terminate(); + terminated = true; + } + }); + } + loop.AddTaskObserver(0, obs); + loop.Run(); + ASSERT_EQ(task_count, count); + ASSERT_EQ(obs_count, count); + started = true; + }); + thread.join(); + ASSERT_TRUE(started); + ASSERT_TRUE(terminated); +} + +} // namespace message_loop_test +} // namespace fml \ No newline at end of file diff --git a/fml/platform/ohos/message_loop_ohos_test.h b/fml/platform/ohos/message_loop_ohos_test.h new file mode 100644 index 0000000000000000000000000000000000000000..f0e74721e39d4d9bc2b0dd634c175256363b1681 --- /dev/null +++ b/fml/platform/ohos/message_loop_ohos_test.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_FML_PLATFORM_OHOS_LOOP_TEST_H_ +#define FLUTTER_FML_PLATFORM_OHOS_LOOP_TEST_H_ + +namespace fml { +namespace message_loop_test { + +void MessageLoopTestPostTask(void); +void MessageLoopTestObserverFire(void); + +} // namespace message_loop_test +} // namespace fml + +#endif // FLUTTER_FML_PLATFORM_OHOS_LOOP_TEST_H_ diff --git a/fml/platform/ohos/napi_util.cc b/fml/platform/ohos/napi_util.cc new file mode 100644 index 0000000000000000000000000000000000000000..ed405322a99da4adb9f75f57873135b5e21b9490 --- /dev/null +++ b/fml/platform/ohos/napi_util.cc @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/fml/platform/ohos/napi_util.h" +#include +#include +#include + +namespace fml { +namespace napi { + +bool NapiIsNull(napi_env env, napi_value value) { + return value == nullptr || NapiIsType(env, value, napi_null); +} +bool NapiIsNotType(napi_env env, napi_value value, napi_valuetype type) { + return !NapiIsType(env, value, type); +} +std::string NapiGetLastError(napi_env env, napi_status status) { + std::string ret("Napi Error:"); + ret += status; + + const napi_extended_error_info* error_info; + napi_get_last_error_info(env, &error_info); + ret += error_info->error_message; + return ret; +} +bool NapiIsType(napi_env env, napi_value value, napi_valuetype type) { + napi_status status; + napi_valuetype argType; + status = napi_typeof(env, value, &argType); + return status == napi_ok && type == argType; +} +bool NapiIsAnyType(napi_env env, napi_value value, ...) { + napi_status status; + napi_valuetype argType; + status = napi_typeof(env, value, &argType); + + if (status != napi_ok) { + return false; + } + + bool matched = false; + { + va_list types; + va_start(types, value); + napi_valuetype cur; + while (!matched) { + cur = va_arg(types, napi_valuetype); + matched = (cur == argType); + } + va_end(types); + } + return matched; +} +void NapiPrintValueTypes(napi_env env, int argc, napi_value* args) { + FML_DLOG(INFO) << "argc: " << argc; + for (int i = 0; i < argc; i++) { + napi_value cur = args[i]; + FML_DLOG(INFO) << "arg: " << i << ",null?" << (cur == nullptr); + if (cur != nullptr) { + NapiPrintValueType(env, cur); + } + } +} + +bool IsArrayBuffer(napi_env env, napi_value value) { + napi_status status; + bool result; + + // Check if the value is an ArrayBuffer + status = napi_is_arraybuffer(env, value, &result); + if (status != napi_ok) { + FML_DLOG(INFO) << "napi_is_arraybuffer: failed:" << status; + return false; + } + + // If it's not an ArrayBuffer, check if it's an Array + if (!result) { + status = napi_is_array(env, value, &result); + if (status != napi_ok) { + FML_DLOG(INFO) << "napi_is_array: failed:" + << NapiGetLastError(env, status); + return false; + } + } + return true; +} + +void NapiPrintValueType(napi_env env, napi_value cur) { + napi_status status; + napi_valuetype argType; + int i = 0; + status = napi_typeof(env, cur, &argType); + if (status != napi_ok) { + FML_DLOG(INFO) << "args,gettype: failed:" << status; + return; + } + if (argType == napi_number) { + FML_DLOG(INFO) << "args,type: " << argType << " number"; + } else if (argType == napi_string) { + FML_DLOG(INFO) << "args[" << i << "],type: " << argType << " napi_string"; + } else if (argType == napi_boolean) { + FML_DLOG(INFO) << "args[" << i << "],type: " << argType << " napi_boolean "; + } else if (argType == napi_object) { + FML_DLOG(INFO) << "args[" << i << "],type: " << argType << " napi_object"; + } else if (argType == napi_function) { + FML_DLOG(INFO) << "args[" << i << "],type: " << argType << " napi_function"; + } else if (argType == napi_null) { + FML_DLOG(INFO) << "args[" << i << "],type: " << argType << " napi_null "; + } else if (argType == napi_symbol) { + FML_DLOG(INFO) << "args[" << i << "],type: " << argType << " napi_symbol"; + } else if (argType == napi_external) { + FML_DLOG(INFO) << "args[" << i << "],type: " << argType << " napi_external"; + } else if (argType == napi_bigint) { + FML_DLOG(INFO) << "args[" << i << "],type: " << argType << " napi_bigint"; + } else { + FML_DLOG(INFO) << "args[" << i << "],type: " << argType << " number"; + } + bool ok = false; + napi_is_array(env, cur, &ok); + if (ok) { + FML_DLOG(INFO) << "args[" << i << "],type: " << argType << " is_array "; + } + if (IsArrayBuffer(env, cur)) { + FML_DLOG(INFO) << "args[" << i << "],type: " << argType + << " is_arraybuffer "; + } + napi_is_typedarray(env, cur, &ok); + if (ok) { + FML_DLOG(INFO) << "args[" << i << "],type: " << argType << " is_typearray"; + } +} + +int32_t GetString(napi_env env, napi_value arg, std::string& strValue) { + napi_status status; + + if (NapiIsType(env, arg, napi_null)) { + FML_DLOG(INFO) << "napi_null"; + strValue = ""; + return kSuccess; + } + if (!NapiIsType(env, arg, napi_string)) { + FML_DLOG(ERROR) << "Invalid type:"; + return kErrorType; + } + + // 获取字符串长度 + size_t str_len; + status = napi_get_value_string_utf8(env, arg, nullptr, 0, &str_len); + if (status != napi_ok) { + FML_DLOG(ERROR) << "Error get str_len:" << status; + FML_DLOG(ERROR) << "result str_len:" << str_len; + return status; + } + + // 读取字符串 + size_t copy_lenth = str_len + 1; + std::vector buff(copy_lenth); + status = napi_get_value_string_utf8(env, arg, static_cast(buff.data()), copy_lenth, ©_lenth); + if (status != napi_ok) { + FML_DLOG(ERROR) << "Error get string:" << status; + FML_DLOG(ERROR) << "result size:" << copy_lenth; + return status; + } + strValue.assign(buff.data(), copy_lenth); + return kSuccess; +} + +int32_t GetArrayString(napi_env env, + napi_value arg, + std::vector& arrayString) { + uint32_t arrayLenth; + napi_status status = napi_get_array_length(env, arg, &arrayLenth); + if (status != napi_ok) { + FML_DLOG(ERROR) << "Failed to napi_get_array_length:" << status; + return status; + } + FML_DLOG(INFO) << "GetArrayString length:" << arrayLenth; + for (uint32_t i = 0; i < arrayLenth; i++) { + napi_value element; + napi_get_element(env, arg, i, &element); + std::string str; + GetString(env, element, str); + arrayString.push_back(str); + } + return kSuccess; +} +int32_t GetArrayBuffer(napi_env env, + napi_value arg, + void** message, + size_t* lenth) { + napi_status status; + if (NapiIsType(env, arg, napi_null)) { + FML_DLOG(ERROR) << "GetArrayBuffer value is null :"; + return kErrorNull; + } + + if (!IsArrayBuffer(env, arg)) { + NapiPrintValueType(env, arg); + return napi_invalid_arg; + } + + status = napi_get_arraybuffer_info(env, arg, message, lenth); + if (status != napi_ok) { + FML_DLOG(ERROR) << "napi_get_arraybuffer_info " + << NapiGetLastError(env, status); + return status; + } + if (*message == nullptr) { + return kErrorNull; + } + + return kSuccess; +} + +napi_value CreateArrayBuffer(napi_env env, void* inputData, size_t dataSize) { + void* data = nullptr; + napi_value arrayBuffer = nullptr; + napi_value result = nullptr; + if (inputData == nullptr) { + return nullptr; + } + napi_status status = + napi_create_arraybuffer(env, dataSize, &data, &arrayBuffer); + if (status) { + FML_DLOG(ERROR) << "napi_create_arraybuffer error:" << status; + return result; + } + memcpy(data, inputData, dataSize); + return arrayBuffer; +} + +napi_status InvokeJsMethod(napi_env env_, + napi_ref ref_napi_obj_, + const char* methodName, + size_t argc, + const napi_value* argv) { + napi_value fn, napi_obj, ret; + napi_status status; + status = napi_get_reference_value(env_, ref_napi_obj_, &napi_obj); + if (status != napi_ok) { + FML_DLOG(ERROR) << "napi_get_reference_value fail "; + return status; + } + + status = napi_get_named_property(env_, napi_obj, methodName, &fn); + if (status != napi_ok) { + FML_DLOG(ERROR) << "napi_get_named_property fail " << methodName; + return status; + } + status = napi_call_function(env_, napi_obj, fn, argc, argv, &ret); + if (status != napi_ok) { + FML_DLOG(ERROR) << "napi_call_function fail " << methodName; + return status; + } + return napi_ok; +} + +} // end namespace napi +} // end namespace fml diff --git a/fml/platform/ohos/napi_util.h b/fml/platform/ohos/napi_util.h new file mode 100644 index 0000000000000000000000000000000000000000..c2cce8198eb95d15f7badcc7628c0d298378199d --- /dev/null +++ b/fml/platform/ohos/napi_util.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 OHOS_FLUTTER_NAPI_UTIL_H +#define OHOS_FLUTTER_NAPI_UTIL_H +#include +#include +#include +#include +#include "flutter/fml/logging.h" +#include "napi/native_api.h" + +namespace fml { +namespace napi { +enum { + kSuccess = 0, + kErrorType = -100, + kErrorNull, +}; + +int32_t GetString(napi_env env, napi_value arg, std::string& strValue); +int32_t GetArrayString(napi_env env, + napi_value arg, + std::vector& arrayString); +int32_t GetArrayBuffer(napi_env env, + napi_value arg, + void** message, + size_t* lenth); + +/** + * 打印napi_value @args 的类型信息 + */ +void NapiPrintValueTypes(napi_env env, int argc, napi_value* argv); +/** + * 打印napi_value @cur 的类型信息 + */ +void NapiPrintValueType(napi_env env, napi_value cur); + +/** + * 判断napi value 类型 + */ +bool NapiIsType(napi_env env, napi_value value, napi_valuetype type); +bool NapiIsNotType(napi_env env, napi_value value, napi_valuetype type); +/** + * 判断napi value类型是否是其中的一种 + */ +bool NapiIsAnyType(napi_env env, napi_value value, ...); +/** + * 判断值是否为空 + */ +bool NapiIsNull(napi_env env, napi_value value); + +/** + * 创建ArrayBuffer + */ +napi_value CreateArrayBuffer(napi_env env, void* inputData, size_t dataSize); + +/** + * 回调JS方法 + */ +napi_status InvokeJsMethod(napi_env env_, + napi_ref ref_napi_obj_, + const char* methodName, + size_t argc, + const napi_value* argv); +} // namespace napi +} // namespace fml +#endif diff --git a/fml/platform/ohos/ohos_trace_event.cc b/fml/platform/ohos/ohos_trace_event.cc new file mode 100644 index 0000000000000000000000000000000000000000..6bb0ab1993d1e7b95fe6d65a1228750a708965ff --- /dev/null +++ b/fml/platform/ohos/ohos_trace_event.cc @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 "flutter/fml/trace_event.h" + +#include + +#if defined(FML_OS_OHOS) +namespace fml { +namespace tracing { + +static constexpr char OHOS_COLON[] = ":"; +static constexpr char OHOS_SCOPE[] = "::"; +static constexpr char OHOS_WHITESPACE[] = " "; +static constexpr char OHOS_FILTER_NAME_SCENE[] = "SceneDisplayLag"; +static constexpr char OHOS_FILTER_NAME_POINTER[] = "PointerEvent"; + +void OHOSTraceTimelineEvent(TraceArg category_group, + TraceArg name, + TraceIDArg id, + Dart_Timeline_Event_Type type, + intptr_t argument_count, + const char** argument_names, + const char** argument_values) { + if (type != Dart_Timeline_Event_Begin && + type != Dart_Timeline_Event_Async_Begin && + type != Dart_Timeline_Event_Async_End && + type != Dart_Timeline_Event_Flow_Begin && + type != Dart_Timeline_Event_Flow_End) { + return; + } + + if (type != Dart_Timeline_Event_Begin && + strcmp(name, OHOS_FILTER_NAME_POINTER) == 0) { + // Trace 'PointerEvent' is not work in the scenario of extenal texture + return; + } + + int realNumber = argument_count; + if (type != Dart_Timeline_Event_Begin && + strcmp(name, OHOS_FILTER_NAME_SCENE) == 0) { + // Trace 'SceneDisplayLag' have inconsistent parameters. It's not good to + // watch. + realNumber = 0; + } + + std::string TraceName(category_group); + TraceName.append(OHOS_SCOPE); + TraceName.append(name); + + if (argument_names != nullptr && argument_values != nullptr) { + for (int i = 0; i < realNumber; i++) { + std::string TraceParam(OHOS_WHITESPACE); + TraceName += + TraceParam + argument_names[i] + OHOS_COLON + argument_values[i]; + } + } + + switch (type) { + case Dart_Timeline_Event_Begin: + OH_HiTrace_StartTrace(TraceName.c_str()); + break; + case Dart_Timeline_Event_Async_Begin: + case Dart_Timeline_Event_Flow_Begin: + OH_HiTrace_StartAsyncTrace(TraceName.c_str(), id); + break; + case Dart_Timeline_Event_Async_End: + case Dart_Timeline_Event_Flow_End: + OH_HiTrace_FinishAsyncTrace(TraceName.c_str(), id); + break; + default: + break; + } + return; +} + +void OHOSTraceEventEnd(void) { + OH_HiTrace_FinishTrace(); +} + +} // namespace tracing +} // namespace fml +#endif // FML_OS_OHOS diff --git a/fml/platform/ohos/paths_ohos.cc b/fml/platform/ohos/paths_ohos.cc new file mode 100644 index 0000000000000000000000000000000000000000..056bdd3770dc917d8aaf0025fc1b548eccd9d9a2 --- /dev/null +++ b/fml/platform/ohos/paths_ohos.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/fml/platform/ohos/paths_ohos.h" + +#include "flutter/fml/file.h" + +namespace fml { +namespace paths { + +std::pair GetExecutablePath() { + return {false, ""}; +} + +static std::string gCachesPath; + +void InitializeOhosCachesPath(std::string caches_path) { + gCachesPath = std::move(caches_path); +} + +fml::UniqueFD GetCachesDirectory() { + // If the caches path is not initialized, the FD will be invalid and caching + // will be disabled throughout the system. + return OpenDirectory(gCachesPath.c_str(), false, fml::FilePermission::kRead); +} + +} // namespace paths +} // namespace fml diff --git a/fml/platform/ohos/paths_ohos.h b/fml/platform/ohos/paths_ohos.h new file mode 100644 index 0000000000000000000000000000000000000000..494d1516a128955d749d18317ee627b872d33f02 --- /dev/null +++ b/fml/platform/ohos/paths_ohos.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_FML_PLATFORM_OHOS_PATHS_OHOS_H_ +#define FLUTTER_FML_PLATFORM_OHOS_PATHS_OHOS_H_ + +#include "flutter/fml/macros.h" +#include "flutter/fml/paths.h" + +namespace fml { +namespace paths { + +void InitializeOhosCachesPath(std::string caches_path); + +} // namespace paths +} // namespace fml + +#endif // FLUTTER_FML_PLATFORM_OHOS_PATHS_OHOS_H_ diff --git a/fml/platform/ohos/timerfd.cc b/fml/platform/ohos/timerfd.cc new file mode 100644 index 0000000000000000000000000000000000000000..4329da26b063d393d0888430d33109917bafb0a8 --- /dev/null +++ b/fml/platform/ohos/timerfd.cc @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/fml/platform/ohos/timerfd.h" + +#include +#include +#include + +#include "flutter/fml/eintr_wrapper.h" +#include "flutter/fml/logging.h" + +#if FML_TIMERFD_AVAILABLE == 0 + +#include +#include + +int timerfd_create(int clockid, int flags) { + return syscall(__NR_timerfd_create, clockid, flags); +} + +int timerfd_settime(int ufc, + int flags, + const struct itimerspec* utmr, + struct itimerspec* otmr) { + return syscall(__NR_timerfd_settime, ufc, flags, utmr, otmr); +} + +#endif // FML_TIMERFD_AVAILABLE == 0 + +namespace fml { +constexpr int NSEC_PER_SEC = 1000000000; +bool TimerRearm(int fd, fml::TimePoint time_point) { + uint64_t nano_secs = time_point.ToEpochDelta().ToNanoseconds(); + + // "0" will disarm the timer, desired behavior is to immediately + // trigger the timer. + if (nano_secs < 1) { + nano_secs = 1; + } + + struct itimerspec spec = {}; + spec.it_value.tv_sec = static_cast(nano_secs / NSEC_PER_SEC); + spec.it_value.tv_nsec = nano_secs % NSEC_PER_SEC; + spec.it_interval = spec.it_value; // single expiry. + + int result = ::timerfd_settime(fd, TFD_TIMER_ABSTIME, &spec, nullptr); + if (result != 0) { + FML_DLOG(ERROR) << "timerfd_settime err:" << strerror(errno); + } + return result == 0; +} + +bool TimerDrain(int fd) { + // 8 bytes must be read from a signaled timer file descriptor when signaled. + uint64_t fire_count = 0; + ssize_t size = FML_HANDLE_EINTR(::read(fd, &fire_count, sizeof(uint64_t))); + if (size != sizeof(uint64_t)) { + return false; + } + return fire_count > 0; +} + +} // namespace fml diff --git a/fml/platform/ohos/timerfd.h b/fml/platform/ohos/timerfd.h new file mode 100644 index 0000000000000000000000000000000000000000..b2715d83f63faf46136f2abc506dcf046e5b8467 --- /dev/null +++ b/fml/platform/ohos/timerfd.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_FML_PLATFORM_OHOS_TIMER_FD_H_ +#define FLUTTER_FML_PLATFORM_OHOS_TIMER_FD_H_ + +#include "flutter/fml/time/time_point.h" + +// clang-format off +#if __has_include() && \ + (!defined(__ANDROID_API__) || __ANDROID_API__ >= 19) + // sys/timerfd.h is always present in Android NDK due to unified headers, + // but timerfd functions are only available on API 19 or later. +// clang-format on + +#include + +#define FML_TIMERFD_AVAILABLE 1 + +#else // __has_include() + +#define FML_TIMERFD_AVAILABLE 0 + +#include +// Must come after sys/types +#include + +#define TFD_TIMER_ABSTIME (1 << 0) +#define TFD_TIMER_CANCEL_ON_SET (1 << 1) + +#define TFD_CLOEXEC O_CLOEXEC +#define TFD_NONBLOCK O_NONBLOCK + +int timerfd_create(int clockid, int flags); + +int timerfd_settime(int ufc, + int flags, + const struct itimerspec* utmr, + struct itimerspec* otmr); + +#endif // __has_include() + +namespace fml { + +/// Rearms the timer to expire at the given time point. +bool TimerRearm(int fd, fml::TimePoint time_point); + +/// Drains the timer FD and returns true if it has expired. This may be false in +/// case the timer read is non-blocking and this routine was called before the +/// timer expiry. +bool TimerDrain(int fd); + +} // namespace fml + +#endif // FLUTTER_FML_PLATFORM_OHOS_TIMER_FD_H_ diff --git a/fml/status_or.h b/fml/status_or.h index 8c911f93bffe94e4abde8dee3981e8fe00aa456d..4a51b1595343c07f999850a1d5dca4418819a33b 100644 --- a/fml/status_or.h +++ b/fml/status_or.h @@ -77,6 +77,7 @@ class StatusOr { } FML_LOG(FATAL) << "StatusOr::value() called on error Status"; FML_UNREACHABLE(); + return value_.value(); } T& value() { @@ -86,6 +87,7 @@ class StatusOr { } FML_LOG(FATAL) << "StatusOr::value() called on error Status"; FML_UNREACHABLE(); + return value_.value(); } private: diff --git a/fml/thread.cc b/fml/thread.cc index 8fc43592d06d3d703579e58b7a4934f8437ebd7a..6d48b0d035dff836233fffb38854b9224936f4bd 100644 --- a/fml/thread.cc +++ b/fml/thread.cc @@ -13,6 +13,7 @@ #include "flutter/fml/build_config.h" #include "flutter/fml/message_loop.h" #include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/fml/logging.h" #if defined(FML_OS_WIN) #include @@ -107,12 +108,13 @@ void SetThreadName(const std::string& name) { } #if defined(FML_OS_MACOSX) pthread_setname_np(name.c_str()); -#elif defined(FML_OS_LINUX) || defined(FML_OS_ANDROID) +#elif defined(FML_OS_LINUX) || defined(FML_OS_ANDROID) || defined(FML_OS_OHOS) // Linux thread names are limited to 16 characters including the terminating // null. constexpr std::string::size_type kLinuxMaxThreadNameLen = 15; pthread_setname_np(pthread_self(), name.substr(0, kLinuxMaxThreadNameLen).c_str()); + FML_DLOG(INFO) << "set the thread name to '" << name; #elif defined(FML_OS_WIN) THREADNAME_INFO info; info.dwType = 0x1000; diff --git a/fml/thread_unittests.cc b/fml/thread_unittests.cc index 8e596edee4618a5721ab9e79d1dca717c1ce49fa..42e9faebac6b2363527d09eb7c09e477d2224642 100644 --- a/fml/thread_unittests.cc +++ b/fml/thread_unittests.cc @@ -5,7 +5,7 @@ #include "flutter/fml/build_config.h" #include "flutter/fml/thread.h" -#if defined(FML_OS_MACOSX) || defined(FML_OS_LINUX) || defined(FML_OS_ANDROID) +#if defined(FML_OS_MACOSX) || defined(FML_OS_LINUX) || defined(FML_OS_ANDROID) || defined(FML_OS_OHOS) #define FLUTTER_PTHREAD_SUPPORTED 1 #else #define FLUTTER_PTHREAD_SUPPORTED 0 diff --git a/fml/trace_event.cc b/fml/trace_event.cc index 28e7105f0aec03327b8affb86bd67eed84a275f5..b69b7266083a4d70a9dff8f8132c18c5f63489b2 100644 --- a/fml/trace_event.cc +++ b/fml/trace_event.cc @@ -100,6 +100,11 @@ void TraceTimelineEvent(TraceArg category_group, const_cast(c_names.data()), // argument_names c_values.data() // argument_values ); +#if defined(FML_OS_OHOS) + OHOSTraceTimelineEvent(category_group, name, 0, type, argument_count, + const_cast(c_names.data()), + c_values.data()); +#endif } void TraceTimelineEvent(TraceArg category_group, @@ -136,6 +141,10 @@ void TraceEvent0(TraceArg category_group, nullptr, // argument_names nullptr // argument_values ); +#if defined(FML_OS_OHOS) + OHOSTraceTimelineEvent(category_group, name, 0, Dart_Timeline_Event_Begin, 0, + nullptr, nullptr); +#endif } void TraceEvent1(TraceArg category_group, @@ -156,6 +165,10 @@ void TraceEvent1(TraceArg category_group, arg_names, // argument_names arg_values // argument_values ); +#if defined(FML_OS_OHOS) + OHOSTraceTimelineEvent(category_group, name, 0, Dart_Timeline_Event_Begin, 1, + arg_names, arg_values); +#endif } void TraceEvent2(TraceArg category_group, @@ -178,6 +191,10 @@ void TraceEvent2(TraceArg category_group, arg_names, // argument_names arg_values // argument_values ); +#if defined(FML_OS_OHOS) + OHOSTraceTimelineEvent(category_group, name, 0, Dart_Timeline_Event_Begin, 2, + arg_names, arg_values); +#endif } void TraceEventEnd(TraceArg name) { @@ -191,6 +208,9 @@ void TraceEventEnd(TraceArg name) { nullptr, // argument_names nullptr // argument_values ); +#if defined(FML_OS_OHOS) + OHOSTraceEventEnd(); +#endif } void TraceEventAsyncBegin0(TraceArg category_group, @@ -208,6 +228,10 @@ void TraceEventAsyncBegin0(TraceArg category_group, nullptr, // argument_names nullptr // argument_values ); +#if defined(FML_OS_OHOS) + OHOSTraceTimelineEvent(category_group, name, id, + Dart_Timeline_Event_Async_Begin, 0, nullptr, nullptr); +#endif } void TraceEventAsyncEnd0(TraceArg category_group, @@ -223,6 +247,10 @@ void TraceEventAsyncEnd0(TraceArg category_group, nullptr, // argument_names nullptr // argument_values ); +#if defined(FML_OS_OHOS) + OHOSTraceTimelineEvent(category_group, name, id, + Dart_Timeline_Event_Async_End, 0, nullptr, nullptr); +#endif } void TraceEventAsyncBegin1(TraceArg category_group, @@ -244,6 +272,11 @@ void TraceEventAsyncBegin1(TraceArg category_group, arg_names, // argument_names arg_values // argument_values ); +#if defined(FML_OS_OHOS) + OHOSTraceTimelineEvent(category_group, name, id, + Dart_Timeline_Event_Async_Begin, 1, arg_names, + arg_values); +#endif } void TraceEventAsyncEnd1(TraceArg category_group, @@ -263,6 +296,11 @@ void TraceEventAsyncEnd1(TraceArg category_group, arg_names, // argument_names arg_values // argument_values ); +#if defined(FML_OS_OHOS) + OHOSTraceTimelineEvent(category_group, name, id, + Dart_Timeline_Event_Async_End, 1, arg_names, + arg_values); +#endif } void TraceEventInstant0(TraceArg category_group, @@ -336,6 +374,10 @@ void TraceEventFlowBegin0(TraceArg category_group, nullptr, // argument_names nullptr // argument_values ); +#if defined(FML_OS_OHOS) + OHOSTraceTimelineEvent(category_group, name, id, + Dart_Timeline_Event_Flow_Begin, 0, nullptr, nullptr); +#endif } void TraceEventFlowStep0(TraceArg category_group, @@ -364,6 +406,10 @@ void TraceEventFlowEnd0(TraceArg category_group, TraceArg name, TraceIDArg id) { nullptr, // argument_names nullptr // argument_values ); +#if defined(FML_OS_OHOS) + OHOSTraceTimelineEvent(category_group, name, id, Dart_Timeline_Event_Flow_End, + 0, nullptr, nullptr); +#endif } #else // FLUTTER_TIMELINE_ENABLED diff --git a/fml/trace_event.h b/fml/trace_event.h index 415fad05aaccb9580381ee53c56c1c7393bfff5a..8cdff1b2cae8916ac67e434378e9cae117654510 100644 --- a/fml/trace_event.h +++ b/fml/trace_event.h @@ -18,7 +18,7 @@ // TODO(DNO-448): This is disabled because the Fuchsia counter id json parsing // only handles ints whereas this can produce ints or strings. #define FML_TRACE_COUNTER(a, b, c, arg1, ...) \ - ::fml::tracing::TraceCounterNopHACK((a), (b), (c), (arg1), __VA_ARGS__); + ::fml::tracing::TraceCounterNopHACK((a), (b), (c), (arg1), __VA_ARGS__) #define FML_TRACE_EVENT(a, b, args...) TRACE_DURATION(a, b) // On Fuchsia, the flow_id arguments to this macro are ignored. @@ -53,6 +53,54 @@ #endif // defined(OS_FUCHSIA) +#if defined(FML_OS_OHOS) + +#include +#include +#include + +class OHFlutterTrace { + public: + explicit OHFlutterTrace(const char* fmt, ...) { + char title[1000] = "OHFlutterTrace"; + const char* title_ = title; + if (fmt != nullptr) { + va_list args; + va_start(args, fmt); + int32_t ret = vsnprintf(title, 1000, fmt, args); + va_end(args); + if (ret != -1) { + title_ = title; + } else { + title_ = "OHFlutterTraceFmt Format Error"; + } + } + OH_HiTrace_StartTrace(title_); + } + ~OHFlutterTrace() { OH_HiTrace_FinishTrace(); } +}; + +#define TRACE_NAME(x, y) x##y +#define TRACE_DURATION_LINE(line, fmt, args...) \ + OHFlutterTrace TRACE_NAME(__OH_trace_, line)(fmt, args) +#define TRACE_DURATION(fmt, args...) TRACE_DURATION_LINE(__LINE__, fmt, args) + +#define TRACE_FMT0 "%s::%s" +#define TRACE_FMT1 "%s::%s %s:%s" +#define TRACE_FMT2 "%s::%s %s:%s %s:%s" +#define OH_TRACE_DURATION0(a, b) TRACE_DURATION(TRACE_FMT0, a, b) +#define OH_TRACE_DURATION1(a, b, c, d) TRACE_DURATION(TRACE_FMT1, a, b, c, d) +#define OH_TRACE_DURATION2(a, b, c, d, e, f) \ + TRACE_DURATION(TRACE_FMT2, a, b, c, d, e, f) + +#else + +#define OH_TRACE_DURATION0(a, b) +#define OH_TRACE_DURATION1(a, b, c, d) +#define OH_TRACE_DURATION2(a, b, c, d, e, f) + +#endif // defined(FML_OS_OHOS) + #include #include #include @@ -63,7 +111,8 @@ #include "flutter/fml/time/time_point.h" #include "third_party/dart/runtime/include/dart_tools_api.h" -#if (FLUTTER_RELEASE && !defined(OS_FUCHSIA) && !defined(FML_OS_ANDROID)) +#if (FLUTTER_RELEASE && !defined(OS_FUCHSIA) && !defined(FML_OS_ANDROID) && \ + !defined(FML_OS_OHOS)) #define FLUTTER_TIMELINE_ENABLED 0 #else #define FLUTTER_TIMELINE_ENABLED 1 @@ -76,7 +125,7 @@ #define __FML__TOKEN_CAT__2(x, y) __FML__TOKEN_CAT__(x, y) #define __FML__AUTO_TRACE_END(name) \ ::fml::tracing::ScopedInstantEnd __FML__TOKEN_CAT__2(__trace_end_, \ - __LINE__)(name); + __LINE__)(name) // This macro has the FML_ prefix so that it does not collide with the macros // from lib/trace/event.h on Fuchsia. @@ -84,7 +133,7 @@ // TODO(chinmaygarde): All macros here should have the FML prefix. #define FML_TRACE_COUNTER(category_group, name, counter_id, arg1, ...) \ ::fml::tracing::TraceCounter((category_group), (name), (counter_id), (arg1), \ - __VA_ARGS__); + __VA_ARGS__) // Avoid using the same `name` and `argX_name` for nested traces, which can // lead to double free errors. E.g. the following code should be avoided: @@ -169,23 +218,26 @@ #define TRACE_EVENT_ASYNC_END1(category_group, name, id, arg1_name, arg1_val) \ ::fml::tracing::TraceEventAsyncEnd1( \ - category_group, name, id, /*flow_id_count=*/0, /*flow_ids=*/nullptr, \ - arg1_name, arg1_val); + category_group, name, id, arg1_name, arg1_val); #define TRACE_EVENT_INSTANT0(category_group, name) \ ::fml::tracing::TraceEventInstant0( \ - category_group, name, /*flow_id_count=*/0, /*flow_ids=*/nullptr); + category_group, name, /*flow_id_count=*/0, /*flow_ids=*/nullptr); \ + OH_TRACE_DURATION0((category_group), (name)) #define TRACE_EVENT_INSTANT1(category_group, name, arg1_name, arg1_val) \ ::fml::tracing::TraceEventInstant1( \ category_group, name, /*flow_id_count=*/0, /*flow_ids=*/nullptr, \ - arg1_name, arg1_val); + arg1_name, arg1_val); \ + OH_TRACE_DURATION1((category_group), (name), (arg1_name), (arg1_val)) #define TRACE_EVENT_INSTANT2(category_group, name, arg1_name, arg1_val, \ arg2_name, arg2_val) \ ::fml::tracing::TraceEventInstant2( \ category_group, name, /*flow_id_count=*/0, /*flow_ids=*/nullptr, \ - arg1_name, arg1_val, arg2_name, arg2_val); + arg1_name, arg1_val, arg2_name, arg2_val); \ + OH_TRACE_DURATION2((category_group), (name), (arg1_name), (arg1_val), \ + (arg2_name), (arg2_val)) #define TRACE_FLOW_BEGIN(category, name, id) \ ::fml::tracing::TraceEventFlowBegin0(category, name, id); @@ -253,6 +305,18 @@ void TraceTimelineEvent(TraceArg category_group, const std::vector& names, const std::vector& values); +#if defined(FML_OS_OHOS) +void OHOSTraceTimelineEvent(TraceArg category_group, + TraceArg name, + TraceIDArg id, + Dart_Timeline_Event_Type type, + intptr_t argument_count, + const char** argument_names, + const char** argument_values); + +void OHOSTraceEventEnd(void); +#endif // FML_OS_OHOS + inline std::string TraceToString(const char* string) { return std::string{string}; } diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index f5e2ce7bf3388abeb6a580225a19ebb75395155a..6458003085ce1b386583553ca700074fc142bb49 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -45,6 +45,10 @@ config("impeller_public_config") { ] } + if (is_ohos) { + defines += [ "OHOS_PLATFORM" ] + } + if (impeller_enable_3d) { defines += [ "IMPELLER_ENABLE_3D" ] } diff --git a/impeller/core/formats.h b/impeller/core/formats.h index aa859b45f68183b56c2c31923fe781ac9d8bd248..505e850ef075e3261a056447f72b53d60af598d8 100644 --- a/impeller/core/formats.h +++ b/impeller/core/formats.h @@ -295,6 +295,7 @@ constexpr bool IsMultisampleCapable(TextureType type) { enum class SampleCount : uint8_t { kCount1 = 1, + kCount2 = 2, kCount4 = 4, }; diff --git a/impeller/docs/assets/shader_pipeline.png b/impeller/docs/assets/shader_pipeline.png new file mode 100644 index 0000000000000000000000000000000000000000..dd0ccc03cbfa327c80bb3e100f8cf75a5efc7978 Binary files /dev/null and b/impeller/docs/assets/shader_pipeline.png differ diff --git a/impeller/docs/assets/xcode_frame_capture/image12.png b/impeller/docs/assets/xcode_frame_capture/image12.png index a90e6e5d6e5e2e9a8d36e1139f6dc79e5ebfffb0..0ee5452194eb90549d903669e0ae66ddb9bf5f68 100644 Binary files a/impeller/docs/assets/xcode_frame_capture/image12.png and b/impeller/docs/assets/xcode_frame_capture/image12.png differ diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index f500d1b75c14c895b06deda66d545c53fe0bb993..f4701545d715e12ae57f029f3b8f0fd21ebc9684 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -238,9 +238,14 @@ static std::unique_ptr CreateDefaultPipeline( // Apply default ContentContextOptions to the descriptor. const auto default_color_format = context.GetCapabilities()->GetDefaultColorFormat(); - ContentContextOptions{.sample_count = SampleCount::kCount4, - .primitive_type = PrimitiveType::kTriangleStrip, - .color_attachment_pixel_format = default_color_format} + ContentContextOptions{ +#ifdef OHOS_PLATFORM + .sample_count = SampleCount::kCount2, +#else + .sample_count = SampleCount::kCount4, +#endif + .primitive_type = PrimitiveType::kTriangleStrip, + .color_attachment_pixel_format = default_color_format} .ApplyToPipelineDescriptor(*desc); return std::make_unique(context, desc); } @@ -267,11 +272,19 @@ ContentContext::ContentContext( } auto options = ContentContextOptions{ +#ifdef OHOS_PLATFORM + .sample_count = SampleCount::kCount2, +#else .sample_count = SampleCount::kCount4, +#endif .color_attachment_pixel_format = context_->GetCapabilities()->GetDefaultColorFormat()}; auto options_trianglestrip = ContentContextOptions{ +#ifdef OHOS_PLATFORM + .sample_count = SampleCount::kCount2, +#else .sample_count = SampleCount::kCount4, +#endif .primitive_type = PrimitiveType::kTriangleStrip, .color_attachment_pixel_format = context_->GetCapabilities()->GetDefaultColorFormat()}; @@ -450,7 +463,11 @@ ContentContext::ContentContext( return; } ContentContextOptions{ +#ifdef OHOS_PLATFORM + .sample_count = SampleCount::kCount2, +#else .sample_count = SampleCount::kCount4, +#endif .color_attachment_pixel_format = context_->GetCapabilities()->GetDefaultColorFormat()} .ApplyToPipelineDescriptor(*clip_pipeline_descriptor); @@ -614,7 +631,11 @@ void ContentContext::InitializeCommonlyUsedShadersIfNeeded() const { // triangle-strip geometry, and also have fairly agressive srcOver to src // blend mode conversions. auto options = ContentContextOptions{ +#ifdef OHOS_PLATFORM + .sample_count = SampleCount::kCount2, +#else .sample_count = SampleCount::kCount4, +#endif .color_attachment_pixel_format = context_->GetCapabilities()->GetDefaultColorFormat()}; diff --git a/impeller/entity/contents/content_context_unittests.cc b/impeller/entity/contents/content_context_unittests.cc index 1736397de0553b0fe8c91ea780af53cf4bd8fc1e..07d8d87ddb5283fe24ba88e2bb3f5fdfad908dc2 100644 --- a/impeller/entity/contents/content_context_unittests.cc +++ b/impeller/entity/contents/content_context_unittests.cc @@ -199,7 +199,10 @@ class FakeCommandBuffer : public CommandBuffer { render_target); } - std::shared_ptr OnCreateBlitPass() override { FML_UNREACHABLE() } + std::shared_ptr OnCreateBlitPass() override { + FML_UNREACHABLE(); + return nullptr; + } virtual bool OnSubmitCommands(CompletionCallback callback) { return true; } @@ -207,6 +210,7 @@ class FakeCommandBuffer : public CommandBuffer { std::shared_ptr OnCreateComputePass() override { FML_UNREACHABLE(); + return nullptr; } }; diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc index 4aadfa3f5ba8fd24fc779e421056faf50ecdafc6..624181c25e855d15fbed3fd57d183f51debf900a 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -629,6 +629,8 @@ std::optional BlendFilterContents::RenderFilter( } FML_UNREACHABLE(); + std::optional ret; + return ret; } } // namespace impeller diff --git a/impeller/entity/contents/filters/inputs/filter_input.cc b/impeller/entity/contents/filters/inputs/filter_input.cc index b1459b7e200ee2daeb96a7a1fd7f965cb20d4347..0364694e16736c0b747799256e69a8bac9c607a5 100644 --- a/impeller/entity/contents/filters/inputs/filter_input.cc +++ b/impeller/entity/contents/filters/inputs/filter_input.cc @@ -39,6 +39,7 @@ FilterInput::Ref FilterInput::Make(Variant input, bool msaa_enabled) { } FML_UNREACHABLE(); + return std::shared_ptr(); } FilterInput::Ref FilterInput::Make(std::shared_ptr texture, diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 5637fd8943313ef0eaa9b4272071b82eba38f36b..2bce5a9997b36cc8cb4e87ddeaad72d012087da5 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -751,6 +751,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( return EntityPass::EntityResult::Success(std::move(element_entity)); } FML_UNREACHABLE(); + return EntityPass::EntityResult::Failure(); } static void SetClipScissor(std::optional clip_coverage, diff --git a/impeller/entity/entity_pass_delegate.cc b/impeller/entity/entity_pass_delegate.cc index 01d75c1924bb0e3a01cdd583faf9f55dfcf3abfe..139897842a1c00e61233cd2f294c2cc8b70be74d 100644 --- a/impeller/entity/entity_pass_delegate.cc +++ b/impeller/entity/entity_pass_delegate.cc @@ -31,7 +31,9 @@ class DefaultEntityPassDelegate final : public EntityPassDelegate { std::shared_ptr target, const Matrix& effect_transform) override { // Not possible since this pass always collapses into its parent. + std::shared_ptr ret; FML_UNREACHABLE(); + return ret; } // |EntityPassDelgate| diff --git a/impeller/entity/shaders/gaussian_blur.glsl b/impeller/entity/shaders/gaussian_blur.glsl new file mode 100644 index 0000000000000000000000000000000000000000..718b4826021c019f50932805689b8f11ebd1af18 --- /dev/null +++ b/impeller/entity/shaders/gaussian_blur.glsl @@ -0,0 +1,70 @@ +// Copyright 2013 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. + +// 1D (directional) gaussian blur. +// +// Paths for future optimization: +// * Remove the uv bounds multiplier in SampleColor by adding optional +// support for SamplerAddressMode::ClampToBorder in the texture sampler. +// * Render both blur passes into a smaller texture than the source image +// (~1/radius size). +// * If doing the small texture render optimization, cache misses can be +// reduced in the first pass by sampling the source textures with a mip +// level of log2(min_radius). + +#include +#include +#include +#include + +uniform sampler2D texture_sampler; +uniform sampler2D alpha_mask_sampler; + +uniform FragInfo { + vec2 texture_size; + vec2 blur_direction; + + // The blur sigma and radius have a linear relationship which is defined + // host-side, but both are useful controls here. Sigma (pixels per standard + // deviation) is used to define the gaussian function itself, whereas the + // radius is used to limit how much of the function is integrated. + float blur_sigma; + float blur_radius; + + float src_factor; + float inner_blur_factor; + float outer_blur_factor; +} +frag_info; + +in vec2 v_texture_coords; +in vec2 v_src_texture_coords; + +out vec4 frag_color; + +void main() { + vec4 total_color = vec4(0); + float gaussian_integral = 0; + vec2 blur_uv_offset = frag_info.blur_direction / frag_info.texture_size; + + for (float i = -frag_info.blur_radius; i <= frag_info.blur_radius; i++) { + float gaussian = IPGaussian(i, frag_info.blur_sigma); + gaussian_integral += gaussian; + total_color += + gaussian * + Sample(texture_sampler, // sampler + v_texture_coords + blur_uv_offset * i // texture coordinates + ); + } + + vec4 blur_color = total_color / gaussian_integral; + + vec4 src_color = Sample(alpha_mask_sampler, // sampler + v_src_texture_coords // texture coordinates + ); + float blur_factor = frag_info.inner_blur_factor * float(src_color.a > 0) + + frag_info.outer_blur_factor * float(src_color.a == 0); + + frag_color = blur_color * blur_factor + src_color * frag_info.src_factor; +} diff --git a/impeller/entity/shaders/gaussian_blur_decal.frag b/impeller/entity/shaders/gaussian_blur_decal.frag new file mode 100644 index 0000000000000000000000000000000000000000..c3076c7f7f902d5728507ce9a53d8a274c94739e --- /dev/null +++ b/impeller/entity/shaders/gaussian_blur_decal.frag @@ -0,0 +1,11 @@ +// Copyright 2013 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. + +#include + +vec4 Sample(sampler2D tex, vec2 coords) { + return IPSampleDecal(tex, coords); +} + +#include "gaussian_blur.glsl" diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 2ff83a294aa59da168127d1bfcf36fd157ea28a9..46c396c494ab5774bb1f0e3ffd6a74729d1ae515 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -412,6 +412,7 @@ struct TRect { return bounds.value(); } FML_UNREACHABLE(); + return {}; } /// @brief Constructs a Matrix that will map all points in the coordinate diff --git a/impeller/playground/playground_impl.cc b/impeller/playground/playground_impl.cc index 5abee6e07e24bd5e3f6b1a901b9ab2e9f850ff56..7cf5418707600593032011e4daf8c0c8811b0ced 100644 --- a/impeller/playground/playground_impl.cc +++ b/impeller/playground/playground_impl.cc @@ -20,6 +20,10 @@ #include "impeller/playground/backend/vulkan/playground_impl_vk.h" #endif // IMPELLER_ENABLE_VULKAN +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wreturn-type" +#endif + namespace impeller { std::unique_ptr PlaygroundImpl::Create( diff --git a/impeller/renderer/backend/BUILD.gn b/impeller/renderer/backend/BUILD.gn index 2f6b15b0d9a55affe34c1baf82111f34dad2dbd7..609d0a22ec7ed1839b584bf8d37e6fbb420d03f4 100644 --- a/impeller/renderer/backend/BUILD.gn +++ b/impeller/renderer/backend/BUILD.gn @@ -13,12 +13,12 @@ group("backend") { } if (impeller_enable_opengles) { - assert(is_mac || is_linux || is_win || is_android) + assert(is_mac || is_linux || is_win || is_android || is_ohos) public_deps += [ "gles" ] } if (impeller_enable_vulkan) { - assert(is_mac || is_linux || is_win || is_android) + assert(is_mac || is_linux || is_win || is_android || is_ohos) public_deps += [ "vulkan" ] } } diff --git a/impeller/renderer/backend/gles/BUILD.gn b/impeller/renderer/backend/gles/BUILD.gn index 5b97e1e2481e665d90efabb70718bc4f358adb7a..de7c968e3a7c739baa5c0e5cd51ff36d7e3ef531 100644 --- a/impeller/renderer/backend/gles/BUILD.gn +++ b/impeller/renderer/backend/gles/BUILD.gn @@ -82,7 +82,7 @@ impeller_component("gles") { "texture_gles.h", ] - if (!is_android && !is_fuchsia) { + if (!is_android && !is_fuchsia && !is_ohos) { public_configs = [ ":gles_config" ] sources += [ "//flutter/third_party/angle/include/GLES2/gl2ext.h", diff --git a/impeller/renderer/backend/gles/gpu_tracer_gles.cc b/impeller/renderer/backend/gles/gpu_tracer_gles.cc index 6e1e167877b3d3c89ab9108e1bc65bc8890b095a..0f77928ccad99f8630ad102cdde3ca2ea3c3f028 100644 --- a/impeller/renderer/backend/gles/gpu_tracer_gles.cc +++ b/impeller/renderer/backend/gles/gpu_tracer_gles.cc @@ -64,6 +64,10 @@ void GPUTracerGLES::ProcessQueries(const ProcTableGLES& gl) { gl.GetQueryObjectui64vEXT(query, GL_QUERY_RESULT_EXT, &duration); auto gpu_ms = duration / 1000000.0; + TRACE_EVENT0("flutter-GPUTracer", + std::string(std::to_string(reinterpret_cast(this)) + + "-FrameTimeMS-" + std::to_string(gpu_ms)) + .c_str()); FML_TRACE_COUNTER("flutter", "GPUTracer", reinterpret_cast(this), // Trace Counter ID "FrameTimeMS", gpu_ms); diff --git a/impeller/renderer/backend/gles/texture_gles.cc b/impeller/renderer/backend/gles/texture_gles.cc index 23e867bc069dff573c57ae08db88d680219cc74a..67f564d0f2e33a62511c08405b76fed7cd3081b1 100644 --- a/impeller/renderer/backend/gles/texture_gles.cc +++ b/impeller/renderer/backend/gles/texture_gles.cc @@ -46,7 +46,7 @@ static TextureGLES::Type GetTextureTypeFromDescriptor( const TextureDescriptor& desc) { const auto usage = static_cast(desc.usage); const auto render_target = TextureUsage::kRenderTarget; - const auto is_msaa = desc.sample_count == SampleCount::kCount4; + const auto is_msaa = desc.sample_count != SampleCount::kCount1; if (usage == render_target && IsDepthStencilFormat(desc.format)) { return is_msaa ? TextureGLES::Type::kRenderBufferMultisampled : TextureGLES::Type::kRenderBuffer; diff --git a/impeller/renderer/backend/vulkan/BUILD.gn b/impeller/renderer/backend/vulkan/BUILD.gn index 8a00a9c77c1afaa2ec9cf9107344fd6804dbe28e..db4ba7911efa5a80f963f7d24aba7712bcb922d7 100644 --- a/impeller/renderer/backend/vulkan/BUILD.gn +++ b/impeller/renderer/backend/vulkan/BUILD.gn @@ -132,6 +132,17 @@ impeller_component("vulkan") { ] } + if (is_ohos) { + sources += [ + "ohos/ohb_texture_source_vk.cc", + "ohos/ohb_texture_source_vk.h", + ] + libs = [ + "native_window", + "native_buffer", + ] + } + public_deps = [ "../../:renderer", "../../../shader_archive", diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.cc b/impeller/renderer/backend/vulkan/capabilities_vk.cc index b5ad0aba23f93a5d08f4b98065e58e1f505e8ae9..3e959fea9d53bee39dbd9b0ac262d735eb91547a 100644 --- a/impeller/renderer/backend/vulkan/capabilities_vk.cc +++ b/impeller/renderer/backend/vulkan/capabilities_vk.cc @@ -112,6 +112,11 @@ CapabilitiesVK::GetEnabledInstanceExtensions() const { has_wsi = true; } + if (HasExtension("VK_OHOS_surface")) { + required.push_back("VK_OHOS_surface"); + has_wsi = true; + } + if (HasExtension("VK_KHR_xcb_surface")) { required.push_back("VK_KHR_xcb_surface"); has_wsi = true; @@ -181,6 +186,26 @@ static const char* GetExtensionName(RequiredAndroidDeviceExtensionVK ext) { FML_UNREACHABLE(); } +static const char* GetExtensionName(RequiredOHOSDeviceExtensionVK ext) { + switch (ext) { + case RequiredOHOSDeviceExtensionVK::kOHOSNativeBuffer: + return "VK_OHOS_native_buffer"; + case RequiredOHOSDeviceExtensionVK::kKHRSamplerYcbcrConversion: + return VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME; + case RequiredOHOSDeviceExtensionVK::kOHOSExternalMemory: + return "VK_OHOS_external_memory"; + case RequiredOHOSDeviceExtensionVK::kEXTQueueFamilyForeign: + return VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME; + case RequiredOHOSDeviceExtensionVK::kKHRDedicatedAllocation: + return VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME; + case RequiredOHOSDeviceExtensionVK::kKHRExternalSemaphoreFd: + return VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME; + case RequiredOHOSDeviceExtensionVK::kLast: + return "Unknown"; + } + FML_UNREACHABLE(); +} + static const char* GetExtensionName(OptionalDeviceExtensionVK ext) { switch (ext) { case OptionalDeviceExtensionVK::kEXTPipelineCreationFeedback: @@ -255,6 +280,19 @@ CapabilitiesVK::GetEnabledDeviceExtensions( return true; }; + auto for_each_ohos_extension = [&](RequiredOHOSDeviceExtensionVK ext) { +#ifdef FML_OS_OHOS + auto name = GetExtensionName(ext); + if (exts->find(name) == exts->end()) { + VALIDATION_LOG << "Device does not support required OHOS extension: " + << name; + return false; + } + enabled.push_back(name); +#endif // FML_OS_ANDROID + return true; + }; + auto for_each_optional_extension = [&](OptionalDeviceExtensionVK ext) { auto name = GetExtensionName(ext); if (exts->find(name) != exts->end()) { @@ -268,6 +306,8 @@ CapabilitiesVK::GetEnabledDeviceExtensions( for_each_common_extension) && IterateExtensions( for_each_android_extension) && + IterateExtensions( + for_each_ohos_extension) && IterateExtensions(for_each_optional_extension); if (!iterate_extensions) { @@ -307,7 +347,12 @@ static bool PhysicalDeviceSupportsRequiredFormats( static bool HasRequiredProperties(const vk::PhysicalDevice& physical_device) { auto properties = physical_device.getProperties(); if (!(properties.limits.framebufferColorSampleCounts & +#ifdef OHOS_PLATFORM + (vk::SampleCountFlagBits::e1 | vk::SampleCountFlagBits::e2 | + vk::SampleCountFlagBits::e4))) { +#else (vk::SampleCountFlagBits::e1 | vk::SampleCountFlagBits::e4))) { +#endif return false; } return true; @@ -461,6 +506,7 @@ bool CapabilitiesVK::SetPhysicalDevice(const vk::PhysicalDevice& device) { { required_common_device_extensions_.clear(); required_android_device_extensions_.clear(); + required_ohos_device_extensions_.clear(); optional_device_extensions_.clear(); auto exts = GetSupportedDeviceExtensions(device); if (!exts.has_value()) { @@ -480,6 +526,13 @@ bool CapabilitiesVK::SetPhysicalDevice(const vk::PhysicalDevice& device) { } return true; }); + IterateExtensions([&](auto ext) -> bool { + auto ext_name = GetExtensionName(ext); + if (exts->find(ext_name) != exts->end()) { + required_ohos_device_extensions_.insert(ext); + } + return true; + }); IterateExtensions([&](auto ext) -> bool { auto ext_name = GetExtensionName(ext); if (exts->find(ext_name) != exts->end()) { @@ -582,6 +635,11 @@ bool CapabilitiesVK::HasExtension(RequiredAndroidDeviceExtensionVK ext) const { required_android_device_extensions_.end(); } +bool CapabilitiesVK::HasExtension(RequiredOHOSDeviceExtensionVK ext) const { + return required_ohos_device_extensions_.find(ext) != + required_ohos_device_extensions_.end(); +} + bool CapabilitiesVK::HasExtension(OptionalDeviceExtensionVK ext) const { return optional_device_extensions_.find(ext) != optional_device_extensions_.end(); diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.h b/impeller/renderer/backend/vulkan/capabilities_vk.h index f06732b8b206e64ac73a6ef0e3c4378f6cee234f..4eef8506f934e8f75fd735083c2962c413c4ea7f 100644 --- a/impeller/renderer/backend/vulkan/capabilities_vk.h +++ b/impeller/renderer/backend/vulkan/capabilities_vk.h @@ -81,6 +81,60 @@ enum class RequiredAndroidDeviceExtensionVK : uint32_t { kLast, }; +//------------------------------------------------------------------------------ +/// @brief A device extension available on all OHOS platforms. Without +/// the presence of these extensions on OHOS, context creation +/// will fail. +/// +/// Platform agnostic code can still check if these OHOS +/// extensions are present. +/// +enum class RequiredOHOSDeviceExtensionVK : uint32_t { + //---------------------------------------------------------------------------- + /// For importing hardware buffers used in external texture composition. + /// + /// https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/vulkan__ohos_8h-V5 + /// + kOHOSNativeBuffer, + + //---------------------------------------------------------------------------- + /// Dependency of kOHOSNativeBuffer. + /// + /// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_sampler_ycbcr_conversion.html + /// + kKHRSamplerYcbcrConversion, + + //---------------------------------------------------------------------------- + /// Dependency of kOHOSNativeBuffer. + /// + /// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_external_memory.html + /// + kOHOSExternalMemory, + + //---------------------------------------------------------------------------- + /// Dependency of kOHOSNativeBuffer. + /// + /// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_queue_family_foreign.html + /// + kEXTQueueFamilyForeign, + + //---------------------------------------------------------------------------- + /// Dependency of kOHOSNativeBuffer. + /// + /// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_external_semaphore_fd.html + /// + kKHRExternalSemaphoreFd, + + //---------------------------------------------------------------------------- + /// Dependency of kOHOSNativeBuffer. + /// + /// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_dedicated_allocation.html + /// + kKHRDedicatedAllocation, + + kLast, +}; + //------------------------------------------------------------------------------ /// @brief A device extension enabled if available. Subsystems cannot /// assume availability and must check if these extensions are @@ -125,6 +179,8 @@ class CapabilitiesVK final : public Capabilities, bool HasExtension(RequiredAndroidDeviceExtensionVK ext) const; + bool HasExtension(RequiredOHOSDeviceExtensionVK ext) const; + bool HasExtension(OptionalDeviceExtensionVK ext) const; std::optional> GetEnabledLayers() const; @@ -199,8 +255,18 @@ class CapabilitiesVK final : public Capabilities, std::set required_common_device_extensions_; std::set required_android_device_extensions_; + std::set required_ohos_device_extensions_; std::set optional_device_extensions_; +#ifdef OHOS_PLATFORM + // This format is set during swapchain initialization and is used for creating + // offscreen textures. On OHOS, offscreen textures are created before the + // swapchain is initialized due to pipeline preloading. In such cases, the + // texture format is undefined, violating Vulkan specifications. To prevent + // this, a default value is assigned. + mutable PixelFormat default_color_format_ = PixelFormat::kR8G8B8A8UNormInt; +#else mutable PixelFormat default_color_format_ = PixelFormat::kUnknown; +#endif PixelFormat default_stencil_format_ = PixelFormat::kUnknown; PixelFormat default_depth_stencil_format_ = PixelFormat::kUnknown; vk::PhysicalDeviceProperties device_properties_; diff --git a/impeller/renderer/backend/vulkan/command_buffer_vk.cc b/impeller/renderer/backend/vulkan/command_buffer_vk.cc index 62e271f7b195a1e8de80fa35d836a64aeadb6b1c..5c5776cbe25614927ad61c76a5a190e2790766e3 100644 --- a/impeller/renderer/backend/vulkan/command_buffer_vk.cc +++ b/impeller/renderer/backend/vulkan/command_buffer_vk.cc @@ -51,6 +51,7 @@ const std::shared_ptr& CommandBufferVK::GetEncoder() { bool CommandBufferVK::OnSubmitCommands(CompletionCallback callback) { FML_UNREACHABLE() + return false; } void CommandBufferVK::OnWaitUntilScheduled() {} diff --git a/impeller/renderer/backend/vulkan/command_queue_vk.h b/impeller/renderer/backend/vulkan/command_queue_vk.h index 6397391c845c984b68c747d7e2a1ed9976c152a3..8a5ce659a5f277765b1ce5ae463450e5d405a9ca 100644 --- a/impeller/renderer/backend/vulkan/command_queue_vk.h +++ b/impeller/renderer/backend/vulkan/command_queue_vk.h @@ -23,7 +23,6 @@ class CommandQueueVK : public CommandQueue { private: std::weak_ptr context_; - CommandQueueVK(const CommandQueueVK&) = delete; CommandQueueVK& operator=(const CommandQueueVK&) = delete; diff --git a/impeller/renderer/backend/vulkan/context_vk.cc b/impeller/renderer/backend/vulkan/context_vk.cc index ad66d2d5d5b2849a9fafd5e55bc6e636a10e92e4..cfe131a2ce21a5bd579b0f4b03d0511fa29a7610 100644 --- a/impeller/renderer/backend/vulkan/context_vk.cc +++ b/impeller/renderer/backend/vulkan/context_vk.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// FLUTTER_NOLINT: https://github.com/flutter/flutter/issues/68331 +#pragma clang diagnostic ignored "-Wc++11-narrowing" + #include "impeller/renderer/backend/vulkan/context_vk.h" #include "fml/concurrent_message_loop.h" @@ -515,6 +518,22 @@ const vk::Device& ContextVK::GetDevice() const { return device_holder_->device.get(); } +void ContextVK::WaitIdle() const { + // vkDeviceWaitIdle is equivalent to calling vkQueueWaitIdle on all queues. + // We must call vkQueueWaitIdle to acquire the queue lock, ensuring that the + // queue is not accessed concurrently. + if (queues_.graphics_queue) { + queues_.graphics_queue->WaitIdle(); + } + if (queues_.compute_queue) { + queues_.compute_queue->WaitIdle(); + } + if (queues_.transfer_queue) { + queues_.transfer_queue->WaitIdle(); + } + return; +} + const std::shared_ptr ContextVK::GetConcurrentWorkerTaskRunner() const { return raster_message_loop_->GetTaskRunner(); @@ -618,6 +637,10 @@ void ContextVK::InitializeCommonlyUsedShadersIfNeeded() const { auto pass = builder.Build(GetDevice()); } +void ContextVK::DisposeThreadLocalCachedResources() { + command_pool_recycler_->Dispose(); +} + const std::shared_ptr& ContextVK::GetYUVConversionLibrary() const { return yuv_conversion_library_; diff --git a/impeller/renderer/backend/vulkan/context_vk.h b/impeller/renderer/backend/vulkan/context_vk.h index 125ca46f7fea7536c20f32cd6a22c04ffcda5e70..c5a150ea2fe50ecb640d1025c0b4c2656df32e2b 100644 --- a/impeller/renderer/backend/vulkan/context_vk.h +++ b/impeller/renderer/backend/vulkan/context_vk.h @@ -139,6 +139,8 @@ class ContextVK final : public Context, const vk::Device& GetDevice() const; + void WaitIdle() const; + const std::unique_ptr& GetDriverInfo() const; const std::shared_ptr @@ -164,8 +166,12 @@ class ContextVK final : public Context, void RecordFrameEndTime() const; + // |Context| void InitializeCommonlyUsedShadersIfNeeded() const override; + // |Context| + void DisposeThreadLocalCachedResources() override; + private: struct DeviceHolderImpl : public DeviceHolderVK { // |DeviceHolder| diff --git a/impeller/renderer/backend/vulkan/context_vk_unittests.cc b/impeller/renderer/backend/vulkan/context_vk_unittests.cc index c0d41e7ea6da407b9db6c711cbe47a594c2a6257..b940fa12225f448e8ec61062cea72dddb1454407 100644 --- a/impeller/renderer/backend/vulkan/context_vk_unittests.cc +++ b/impeller/renderer/backend/vulkan/context_vk_unittests.cc @@ -72,6 +72,25 @@ TEST(ContextVKTest, DeletesCommandPoolsOnAllThreads) { ASSERT_FALSE(weak_pool_thread.lock()); } +TEST(ContextVKTest, ThreadLocalCleanupDeletesCommandPool) { + std::shared_ptr context = MockVulkanContextBuilder().Build(); + + fml::AutoResetWaitableEvent latch1, latch2; + std::weak_ptr weak_pool; + std::thread thread([&]() { + weak_pool = context->GetCommandPoolRecycler()->Get(); + context->DisposeThreadLocalCachedResources(); + latch1.Signal(); + latch2.Wait(); + }); + + latch1.Wait(); + ASSERT_FALSE(weak_pool.lock()); + + latch2.Signal(); + thread.join(); +} + TEST(ContextVKTest, DeletePipelineAfterContext) { std::shared_ptr> pipeline; std::shared_ptr> functions; diff --git a/impeller/renderer/backend/vulkan/formats_vk.h b/impeller/renderer/backend/vulkan/formats_vk.h index 06d1d8e21b38d040d8a668feac31b4a3aaf749fc..069916253bff5a12fcc09abbfa5a3a4a018a6124 100644 --- a/impeller/renderer/backend/vulkan/formats_vk.h +++ b/impeller/renderer/backend/vulkan/formats_vk.h @@ -22,6 +22,8 @@ constexpr vk::SampleCountFlagBits ToVKSampleCountFlagBits(SampleCount count) { switch (count) { case SampleCount::kCount1: return vk::SampleCountFlagBits::e1; + case SampleCount::kCount2: + return vk::SampleCountFlagBits::e2; case SampleCount::kCount4: return vk::SampleCountFlagBits::e4; } @@ -204,6 +206,8 @@ constexpr vk::SampleCountFlagBits ToVKSampleCount(SampleCount sample_count) { switch (sample_count) { case SampleCount::kCount1: return vk::SampleCountFlagBits::e1; + case SampleCount::kCount2: + return vk::SampleCountFlagBits::e2; case SampleCount::kCount4: return vk::SampleCountFlagBits::e4; } @@ -360,6 +364,7 @@ constexpr vk::IndexType ToVKIndexType(IndexType index_type) { } FML_UNREACHABLE(); + return vk::IndexType::eUint16; } constexpr vk::PolygonMode ToVKPolygonMode(PolygonMode mode) { @@ -370,6 +375,7 @@ constexpr vk::PolygonMode ToVKPolygonMode(PolygonMode mode) { return vk::PolygonMode::eLine; } FML_UNREACHABLE(); + return vk::PolygonMode::eFill; } constexpr vk::PrimitiveTopology ToVKPrimitiveTopology(PrimitiveType primitive) { @@ -387,6 +393,7 @@ constexpr vk::PrimitiveTopology ToVKPrimitiveTopology(PrimitiveType primitive) { } FML_UNREACHABLE(); + return vk::PrimitiveTopology::eTriangleList; } constexpr bool PixelFormatIsDepthStencil(PixelFormat format) { @@ -426,6 +433,7 @@ constexpr vk::CullModeFlags ToVKCullModeFlags(CullMode mode) { return vk::CullModeFlagBits::eBack; } FML_UNREACHABLE(); + return vk::CullModeFlagBits::eNone; } constexpr vk::CompareOp ToVKCompareOp(CompareFunction op) { @@ -448,6 +456,7 @@ constexpr vk::CompareOp ToVKCompareOp(CompareFunction op) { return vk::CompareOp::eGreaterOrEqual; } FML_UNREACHABLE(); + return vk::CompareOp::eNever; } constexpr vk::StencilOp ToVKStencilOp(StencilOperation op) { @@ -471,6 +480,7 @@ constexpr vk::StencilOp ToVKStencilOp(StencilOperation op) { break; } FML_UNREACHABLE(); + return vk::StencilOp::eKeep; } constexpr vk::StencilOpState ToVKStencilOpState( @@ -512,6 +522,7 @@ constexpr vk::ImageAspectFlags ToVKImageAspectFlags(PixelFormat format) { vk::ImageAspectFlagBits::eStencil; } FML_UNREACHABLE(); + return vk::ImageAspectFlagBits::eColor; } constexpr uint32_t ToArrayLayerCount(TextureType type) { @@ -526,6 +537,7 @@ constexpr uint32_t ToArrayLayerCount(TextureType type) { << "kTextureExternalOES can not be used with the Vulkan backend."; } FML_UNREACHABLE(); + return 1u; } constexpr vk::ImageViewType ToVKImageViewType(TextureType type) { @@ -540,6 +552,7 @@ constexpr vk::ImageViewType ToVKImageViewType(TextureType type) { << "kTextureExternalOES can not be used with the Vulkan backend."; } FML_UNREACHABLE(); + return vk::ImageViewType::e2D; } constexpr vk::ImageCreateFlags ToVKImageCreateFlags(TextureType type) { @@ -554,6 +567,7 @@ constexpr vk::ImageCreateFlags ToVKImageCreateFlags(TextureType type) { << "kTextureExternalOES can not be used with the Vulkan backend."; } FML_UNREACHABLE(); + return {}; } vk::PipelineDepthStencilStateCreateInfo ToVKPipelineDepthStencilStateCreateInfo( @@ -586,6 +600,7 @@ constexpr vk::ImageAspectFlags ToImageAspectFlags(PixelFormat format) { vk::ImageAspectFlagBits::eStencil; } FML_UNREACHABLE(); + return {}; } } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/ohos/ohb_texture_source_vk.cc b/impeller/renderer/backend/vulkan/ohos/ohb_texture_source_vk.cc new file mode 100644 index 0000000000000000000000000000000000000000..e6db89edc793cfa4393f3eb70fc5dec7e3f4babe --- /dev/null +++ b/impeller/renderer/backend/vulkan/ohos/ohb_texture_source_vk.cc @@ -0,0 +1,314 @@ +// Copyright 2013 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. + +#include "impeller/renderer/backend/vulkan/ohos/ohb_texture_source_vk.h" +#include "impeller/core/formats.h" +#include "impeller/renderer/backend/vulkan/allocator_vk.h" +#include "impeller/renderer/backend/vulkan/context_vk.h" +#include "impeller/renderer/backend/vulkan/yuv_conversion_library_vk.h" + +#include + +#include +#include +#include + +namespace impeller { + +using ONBProperties = vk::StructureChain; + +static PixelFormat ToPixelFormat(int32_t format) { + if (format < 0 || format > NATIVEBUFFER_PIXEL_FMT_RGBA_1010102) { + return PixelFormat::kR8G8B8A8UNormInt; + } + OH_NativeBuffer_Format format_spec = + static_cast(format); + switch (format_spec) { + case OH_NativeBuffer_Format::NATIVEBUFFER_PIXEL_FMT_RGBA_8888: + return PixelFormat::kR8G8B8A8UNormInt; + case OH_NativeBuffer_Format::NATIVEBUFFER_PIXEL_FMT_BGRA_8888: + return PixelFormat::kB8G8R8A8UNormInt; + default: + // Not understood by the rest of Impeller. Use a placeholder but create + // the native image and image views using the right external format. + break; + } + return PixelFormat::kR8G8B8A8UNormInt; +} + +static TextureDescriptor CreateTextureDescriptorFromNativeWindowBuffer( + OHNativeWindowBuffer* native_window_buffer) { + OH_NativeBuffer_Config nativebuffer_config; + OH_NativeBuffer* native_buffer; + TextureDescriptor descriptor; + descriptor.size = {0, 0}; + int ret = OH_NativeBuffer_FromNativeWindowBuffer(native_window_buffer, + &native_buffer); + if (ret != 0) { + FML_LOG(ERROR) << "OHOSExternalTextureGL get OH_NativeBuffer error:" << ret; + return descriptor; + } + OH_NativeBuffer_GetConfig(native_buffer, &nativebuffer_config); + descriptor.format = ToPixelFormat(nativebuffer_config.format); + descriptor.size = + ISize{nativebuffer_config.width, nativebuffer_config.height}; + descriptor.storage_mode = StorageMode::kDevicePrivate; + descriptor.type = TextureType::kTexture2D; + descriptor.mip_count = 1; + descriptor.sample_count = SampleCount::kCount1; + descriptor.compression_type = CompressionType::kLossless; + return descriptor; +} + +static vk::UniqueImage CreateVkImage( + const vk::Device& device, + OH_NativeBuffer* native_buffer, + const vk::NativeBufferFormatPropertiesOHOS& onb_format) { + VkExternalFormatOHOS externalFormat{ + VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_OHOS, + nullptr, + 0, + }; + if (onb_format.format == vk::Format::eUndefined) { + externalFormat.externalFormat = onb_format.externalFormat; + } + + VkExternalMemoryImageCreateInfo ext_mem_info{ + VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, &externalFormat, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OHOS_NATIVE_BUFFER_BIT_OHOS}; + + OH_NativeBuffer_Config nativebuffer_config; + OH_NativeBuffer_GetConfig(native_buffer, &nativebuffer_config); + + vk::ImageUsageFlags usage_flags = vk::ImageUsageFlagBits::eSampled; + if (nativebuffer_config.usage & NATIVEBUFFER_USAGE_HW_RENDER) { + usage_flags |= vk::ImageUsageFlagBits::eColorAttachment; + } + + vk::ImageCreateInfo image_info; + image_info.pNext = &ext_mem_info; + image_info.flags = vk::ImageCreateFlags(0); + image_info.imageType = vk::ImageType::e2D; + image_info.extent.width = nativebuffer_config.width; + image_info.extent.height = nativebuffer_config.height; + image_info.extent.depth = 1; + image_info.mipLevels = 1; + image_info.arrayLayers = 1; + image_info.format = onb_format.format; + image_info.tiling = vk::ImageTiling::eOptimal; + image_info.initialLayout = vk::ImageLayout::eUndefined; + image_info.usage = usage_flags; + image_info.samples = vk::SampleCountFlagBits::e1; + image_info.sharingMode = vk::SharingMode::eExclusive; + + auto image_result = device.createImageUnique(image_info); + if (image_result.result != vk::Result::eSuccess) { + return {}; + } + return std::move(image_result.value); +} + +static vk::UniqueDeviceMemory AllocateDeviceMemorty( + const std::shared_ptr& context, + const vk::Image& image, + OH_NativeBuffer* native_buffer, + const vk::NativeBufferPropertiesOHOS& ohb_props) { + vk::Device device = context->GetDevice(); + vk::PhysicalDevice physical_device = context->GetPhysicalDevice(); + vk::PhysicalDeviceMemoryProperties memory_properties; + physical_device.getMemoryProperties(&memory_properties); + int memory_type_index = AllocatorVK::FindMemoryTypeIndex( + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, memory_properties); + if (memory_type_index < 0) { + VALIDATION_LOG << "Could not find memory type of external image."; + return {}; + } + + FML_LOG(INFO) << "find memory index " << memory_type_index; + + VkImportNativeBufferInfoOHOS nb_info; + nb_info.sType = VK_STRUCTURE_TYPE_IMPORT_NATIVE_BUFFER_INFO_OHOS; + nb_info.buffer = native_buffer; + nb_info.pNext = nullptr; + + VkMemoryDedicatedAllocateInfo ded_alloc_info; + ded_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; + ded_alloc_info.image = image; + ded_alloc_info.buffer = VK_NULL_HANDLE; + ded_alloc_info.pNext = &nb_info; + + VkMemoryAllocateInfo alloc_info; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = ohb_props.allocationSize; + alloc_info.memoryTypeIndex = memory_type_index; + alloc_info.pNext = &ded_alloc_info; + + auto device_memory = device.allocateMemoryUnique(alloc_info); + + if (device_memory.result != vk::Result::eSuccess) { + FML_LOG(ERROR) << "allocateMemoryUnique failed"; + return {}; + } + return std::move(device_memory.value); +} + +static std::shared_ptr CreateYUVConversion( + const ContextVK& context, + const vk::NativeBufferFormatPropertiesOHOS& onb_format) { + YUVConversionDescriptorVK conversion_chain; + auto& ycbcr_info = conversion_chain.get(); + ycbcr_info.format = onb_format.format; + ycbcr_info.ycbcrModel = onb_format.suggestedYcbcrModel; + ycbcr_info.ycbcrRange = onb_format.suggestedYcbcrRange; + ycbcr_info.components = onb_format.samplerYcbcrConversionComponents; + ycbcr_info.xChromaOffset = onb_format.suggestedXChromaOffset; + ycbcr_info.yChromaOffset = onb_format.suggestedYChromaOffset; + ycbcr_info.chromaFilter = vk::Filter::eNearest; + ycbcr_info.forceExplicitReconstruction = false; + + if (ycbcr_info.format == vk::Format::eUndefined) { + auto& external_format = conversion_chain.get(); + external_format.externalFormat = onb_format.externalFormat; + FML_LOG(INFO) << "set yuv external_format " << (onb_format.externalFormat); + } else { + conversion_chain.unlink(); + FML_LOG(INFO) << "not set yuv external_format"; + } + + return context.GetYUVConversionLibrary()->GetConversion(conversion_chain); +} + +static vk::UniqueImageView CreateVkImageView( + const vk::Device& device, + const vk::Image& image, + const vk::SamplerYcbcrConversion& yuv_conversion, + const vk::NativeBufferFormatPropertiesOHOS& onb_format) { + vk::StructureChain + view_chain; + auto& view_info = view_chain.get(); + view_info.image = image; + view_info.viewType = vk::ImageViewType::e2D; + view_info.format = onb_format.format; + view_info.components.r = vk::ComponentSwizzle::eIdentity; + view_info.components.g = vk::ComponentSwizzle::eIdentity; + view_info.components.b = vk::ComponentSwizzle::eIdentity; + view_info.components.a = vk::ComponentSwizzle::eIdentity; + view_info.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor; + view_info.subresourceRange.baseMipLevel = 0; + view_info.subresourceRange.levelCount = 1; + view_info.subresourceRange.baseArrayLayer = 0; + view_info.subresourceRange.layerCount = 1; + + if (view_info.format == vk::Format::eUndefined) { + view_chain.get().conversion = + yuv_conversion; + } else { + view_chain.unlink(); + FML_LOG(INFO) << "unlink yuv sampler "; + } + auto view_result = device.createImageViewUnique(view_info); + if (view_result.result != vk::Result::eSuccess) { + return {}; + } + return std::move(view_result.value); +} + +OHBTextureSourceVK::OHBTextureSourceVK( + const std::shared_ptr& context, + OHNativeWindowBuffer* native_window_buffer) + : TextureSourceVK( + CreateTextureDescriptorFromNativeWindowBuffer(native_window_buffer)) { + is_valid_ = false; + if (!native_window_buffer) { + return; + } + + vk::Device device = context->GetDevice(); + OH_NativeBuffer* native_buffer = nullptr; + + int ret = OH_NativeBuffer_FromNativeWindowBuffer(native_window_buffer, + &native_buffer); + if (ret != 0 || native_buffer == nullptr) { + FML_LOG(ERROR) << "OHOSExternalTextureGL get OH_NativeBuffer error:" << ret; + return; + } + + ONBProperties onb_props; + auto get_ret = + device.getNativeBufferPropertiesOHOS(native_buffer, &onb_props.get()); + if (get_ret != vk::Result::eSuccess) { + FML_LOG(ERROR) << "getNativeBufferPropertiesOHOS faile " << int(get_ret); + return; + } + + const auto& onb_format = + onb_props.get(); + + FML_LOG(INFO) << "onb_format " << int(onb_format.format) << " external " + << int(onb_format.externalFormat) << " allocSize " + << onb_props.get().allocationSize; + auto image = CreateVkImage(device, native_buffer, onb_format); + if (!image) { + FML_LOG(ERROR) << "create vkimage faile"; + return; + } + + auto device_memory = AllocateDeviceMemorty(context, image_.get(), + native_buffer, onb_props.get()); + if (!device_memory) { + FML_LOG(ERROR) << "allocateDeviceMemorty failed"; + return; + } + + auto bind_ret = device.bindImageMemory(image.get(), device_memory.get(), 0); + if (bind_ret != vk::Result::eSuccess) { + FML_LOG(ERROR) << "bindImageMemory failed " << int(bind_ret); + return; + } + + auto yuv_conversion = CreateYUVConversion(*context, onb_format); + + auto image_view = CreateVkImageView( + device, image.get(), yuv_conversion->GetConversion(), onb_format); + if (!image_view) { + FML_LOG(ERROR) << "CreateVkImageView failed"; + return; + } + needs_yuv_conversion_ = onb_format.format == vk::Format::eUndefined; + device_memory_ = std::move(device_memory); + image_ = std::move(image); + yuv_conversion_ = std::move(yuv_conversion); + image_view_ = std::move(image_view); + is_valid_ = true; +} + +OHBTextureSourceVK::~OHBTextureSourceVK() { + // Vulkan resources are automatically destroyed by vk::Unique* destructors. +} + +vk::Image OHBTextureSourceVK::GetImage() const { + return image_.get(); +} + +vk::ImageView OHBTextureSourceVK::GetImageView() const { + return image_view_.get(); +} + +vk::ImageView OHBTextureSourceVK::GetRenderTargetView() const { + return image_view_.get(); // Assuming same view can be used for render target +} + +bool OHBTextureSourceVK::IsValid() const { + return is_valid_; +} + +bool OHBTextureSourceVK::IsSwapchainImage() const { + return false; +} + +std::shared_ptr OHBTextureSourceVK::GetYUVConversion() const { + return needs_yuv_conversion_ ? yuv_conversion_ : nullptr; +} +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/ohos/ohb_texture_source_vk.h b/impeller/renderer/backend/vulkan/ohos/ohb_texture_source_vk.h new file mode 100644 index 0000000000000000000000000000000000000000..0da8f6bf42c5bab47459c675017415cdeb06fb48 --- /dev/null +++ b/impeller/renderer/backend/vulkan/ohos/ohb_texture_source_vk.h @@ -0,0 +1,64 @@ +// Copyright 2013 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. + +#ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_OHOS_OHB_TEXTURE_SOURCE_VK_H_ +#define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_OHOS_OHB_TEXTURE_SOURCE_VK_H_ + +#include "flutter/fml/macros.h" +#include "impeller/geometry/size.h" +#include "impeller/renderer/backend/vulkan/formats_vk.h" +#include "impeller/renderer/backend/vulkan/texture_source_vk.h" +#include "impeller/renderer/backend/vulkan/vk.h" +#include "impeller/renderer/backend/vulkan/yuv_conversion_vk.h" + +#include "flutter/fml/macros.h" +#include "flutter/fml/native_library.h" + +#include + +namespace impeller { + +class ContextVK; + +class OHBTextureSourceVK final : public TextureSourceVK { + public: + OHBTextureSourceVK(const std::shared_ptr& context, + OHNativeWindowBuffer* native_window_buffer); + + // |TextureSourceVK| + ~OHBTextureSourceVK() override; + + // |TextureSourceVK| + vk::Image GetImage() const override; + + // |TextureSourceVK| + vk::ImageView GetImageView() const override; + + // |TextureSourceVK| + vk::ImageView GetRenderTargetView() const override; + + bool IsValid() const; + + // |TextureSourceVK| + bool IsSwapchainImage() const override; + + // |TextureSourceVK| + std::shared_ptr GetYUVConversion() const override; + + private: + vk::UniqueDeviceMemory device_memory_ = {}; + vk::UniqueImage image_ = {}; + vk::UniqueImageView image_view_ = {}; + std::shared_ptr yuv_conversion_ = {}; + bool needs_yuv_conversion_ = false; + bool is_valid_ = false; + + OHBTextureSourceVK(const OHBTextureSourceVK&) = delete; + + OHBTextureSourceVK& operator=(const OHBTextureSourceVK&) = delete; +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_OHOS_OHB_TEXTURE_SOURCE_VK_H_ diff --git a/impeller/renderer/backend/vulkan/queue_vk.cc b/impeller/renderer/backend/vulkan/queue_vk.cc index c35dc21e6812d0823df0083d83bb3adb99da124d..f977c732a63bf9591bf5705d5094ba5ce0d75244 100644 --- a/impeller/renderer/backend/vulkan/queue_vk.cc +++ b/impeller/renderer/backend/vulkan/queue_vk.cc @@ -28,6 +28,24 @@ vk::Result QueueVK::Present(const vk::PresentInfoKHR& present_info) { return queue_.presentKHR(present_info); } +void QueueVK::WaitIdle() const { + Lock lock(queue_mutex_); + [[maybe_unused]] auto result = queue_.waitIdle(); + return; +} + +#ifdef FML_OS_OHOS +vk::Result QueueVK::QueueSignalReleaseImageOHOS( + std::vector semaphores, + vk::Image image, + int* fence_fd) { + Lock lock(queue_mutex_); + return queue_.queueSignalReleaseImageOHOS((int32_t)semaphores.size(), + (vk::Semaphore*)semaphores.data(), + image, fence_fd); +} +#endif + void QueueVK::InsertDebugMarker(std::string_view label) const { if (!HasValidationLayers()) { return; diff --git a/impeller/renderer/backend/vulkan/queue_vk.h b/impeller/renderer/backend/vulkan/queue_vk.h index 62d650bfc6176da8751f7cdb396932e76b7a771d..f21e88ce42e3de5b2108a31c329147c3e0e9e3ae 100644 --- a/impeller/renderer/backend/vulkan/queue_vk.h +++ b/impeller/renderer/backend/vulkan/queue_vk.h @@ -41,6 +41,14 @@ class QueueVK { vk::Result Present(const vk::PresentInfoKHR& present_info); + void WaitIdle() const; + +#ifdef FML_OS_OHOS + vk::Result QueueSignalReleaseImageOHOS(std::vector semaphores, + vk::Image image, + int* fence_fd); +#endif + void InsertDebugMarker(std::string_view label) const; private: diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.cc b/impeller/renderer/backend/vulkan/render_pass_vk.cc index ba5d68f08e8384bec2b1f8334e994d971a4117e7..205d1c6a991753fa820ace6f9e54a5ba552ebd42 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.cc +++ b/impeller/renderer/backend/vulkan/render_pass_vk.cc @@ -1,6 +1,9 @@ // Copyright 2013 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. +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wreturn-type" +#endif #include "impeller/renderer/backend/vulkan/render_pass_vk.h" diff --git a/impeller/renderer/backend/vulkan/surface_context_vk.cc b/impeller/renderer/backend/vulkan/surface_context_vk.cc index 1b732b509bd0431b7d20401ba8fae9333ad3aebc..f25691935599d265077d4b7eadb79ff34045c43a 100644 --- a/impeller/renderer/backend/vulkan/surface_context_vk.cc +++ b/impeller/renderer/backend/vulkan/surface_context_vk.cc @@ -9,6 +9,7 @@ #include "impeller/renderer/backend/vulkan/context_vk.h" #include "impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.h" #include "impeller/renderer/surface.h" +#include "vulkan/vulkan_core.h" namespace impeller { @@ -77,6 +78,10 @@ bool SurfaceContextVK::SetWindowSurface(vk::UniqueSurfaceKHR surface, return true; } +void SurfaceContextVK::ClearSwapchain() { + swapchain_ = nullptr; +} + std::unique_ptr SurfaceContextVK::AcquireNextSurface() { TRACE_EVENT0("impeller", __FUNCTION__); auto surface = swapchain_ ? swapchain_->AcquireNextDrawable() : nullptr; @@ -119,6 +124,33 @@ vk::UniqueSurfaceKHR SurfaceContextVK::CreateAndroidSurface( #endif // FML_OS_ANDROID +#ifdef FML_OS_OHOS +vk::UniqueSurfaceKHR SurfaceContextVK::CreateOHOSSurface( + OHNativeWindow* window) const { + if (!parent_->GetInstance()) { + VALIDATION_LOG << "createSurface get null instance"; + return vk::UniqueSurfaceKHR{VK_NULL_HANDLE}; + } + static PFN_vkCreateSurfaceOHOS vkCreateSurfaceOHOS = + (PFN_vkCreateSurfaceOHOS)parent_->GetInstance().getProcAddr( + "vkCreateSurfaceOHOS"); + if (!vkCreateSurfaceOHOS) { + VALIDATION_LOG << "missing vkCreateSurfaceOHOS extension"; + return vk::UniqueSurfaceKHR{VK_NULL_HANDLE}; + } + const VkSurfaceCreateInfoOHOS surfaceCreateInfo{ + (VkStructureType)VK_STRUCTURE_TYPE_SURFACE_CREATE_INFO_OHOS, nullptr, 0, + window}; + VkSurfaceKHR surface = VK_NULL_HANDLE; + if (vkCreateSurfaceOHOS(parent_->GetInstance(), &surfaceCreateInfo, nullptr, + &surface) != VK_SUCCESS) { + VALIDATION_LOG << "vkCreateSurfaceOHOS get failed"; + return vk::UniqueSurfaceKHR{VK_NULL_HANDLE}; + } + return vk::UniqueSurfaceKHR(surface, parent_->GetInstance()); +} +#endif // FML_OS_OHOS + const vk::Device& SurfaceContextVK::GetDevice() const { return parent_->GetDevice(); } @@ -131,4 +163,8 @@ const ContextVK& SurfaceContextVK::GetParent() const { return *parent_; } +void SurfaceContextVK::DisposeThreadLocalCachedResources() { + parent_->DisposeThreadLocalCachedResources(); +} + } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/surface_context_vk.h b/impeller/renderer/backend/vulkan/surface_context_vk.h index 799edcb9bb34bf5a9d044f297dd76dbef9b5d5ef..e64aac8fd40118254970fbde28c4d733923e6ea7 100644 --- a/impeller/renderer/backend/vulkan/surface_context_vk.h +++ b/impeller/renderer/backend/vulkan/surface_context_vk.h @@ -12,6 +12,13 @@ #include "impeller/renderer/command_queue.h" #include "impeller/renderer/context.h" +#ifdef FML_OS_OHOS +#include +#include +#include +#define VK_STRUCTURE_TYPE_SURFACE_CREATE_INFO_OHOS 1000451000 +#endif + namespace impeller { class ContextVK; @@ -72,18 +79,28 @@ class SurfaceContextVK : public Context, [[nodiscard]] bool SetWindowSurface(vk::UniqueSurfaceKHR surface, const ISize& size); + void ClearSwapchain(); + std::unique_ptr AcquireNextSurface(); /// @brief Mark the current swapchain configuration as dirty, forcing it to be /// recreated on the next frame. void UpdateSurfaceSize(const ISize& size) const; + // |Context| void InitializeCommonlyUsedShadersIfNeeded() const override; #ifdef FML_OS_ANDROID vk::UniqueSurfaceKHR CreateAndroidSurface(ANativeWindow* window) const; #endif // FML_OS_ANDROID +#ifdef FML_OS_OHOS + vk::UniqueSurfaceKHR CreateOHOSSurface(OHNativeWindow* window) const; +#endif // FML_OS_OHOS + + // |Context| + void DisposeThreadLocalCachedResources() override; + const vk::Device& GetDevice() const; const ContextVK& GetParent() const; diff --git a/impeller/renderer/backend/vulkan/swapchain/khr/khr_surface_vk.cc b/impeller/renderer/backend/vulkan/swapchain/khr/khr_surface_vk.cc index 2fa35f63f37f803dd451b1a56da1e0ed14cff1c7..79806b519700279b1048ab6f7089b55782c19371 100644 --- a/impeller/renderer/backend/vulkan/swapchain/khr/khr_surface_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain/khr/khr_surface_vk.cc @@ -25,7 +25,11 @@ std::unique_ptr KHRSurfaceVK::WrapSwapchainImage( TextureDescriptor msaa_tex_desc; msaa_tex_desc.storage_mode = StorageMode::kDeviceTransient; msaa_tex_desc.type = TextureType::kTexture2DMultisample; +#ifdef OHOS_PLATFORM + msaa_tex_desc.sample_count = SampleCount::kCount2; +#else msaa_tex_desc.sample_count = SampleCount::kCount4; +#endif msaa_tex_desc.format = swapchain_image->GetPixelFormat(); msaa_tex_desc.size = swapchain_image->GetSize(); msaa_tex_desc.usage = TextureUsage::kRenderTarget; diff --git a/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc b/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc index 375c53603835939c7387ecb9cca132ecc25be182..6a4134b0a1230fdc1dc589eb627b46535418e80a 100644 --- a/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc @@ -31,6 +31,7 @@ struct KHRFrameSynchronizerVK { vk::UniqueSemaphore render_ready; vk::UniqueSemaphore present_ready; std::shared_ptr final_cmd_buffer; + std::shared_ptr ready_cmd_buffer; bool is_valid = false; explicit KHRFrameSynchronizerVK(const vk::Device& device) { @@ -233,7 +234,11 @@ KHRSwapchainImplVK::KHRSwapchainImplVK(const std::shared_ptr& context, TextureDescriptor msaa_desc; msaa_desc.storage_mode = StorageMode::kDeviceTransient; msaa_desc.type = TextureType::kTexture2DMultisample; +#ifdef OHOS_PLATFORM + msaa_desc.sample_count = SampleCount::kCount2; +#else msaa_desc.sample_count = SampleCount::kCount4; +#endif msaa_desc.format = texture_desc.format; msaa_desc.size = texture_desc.size; msaa_desc.usage = TextureUsage::kRenderTarget; @@ -245,7 +250,11 @@ KHRSwapchainImplVK::KHRSwapchainImplVK(const std::shared_ptr& context, depth_stencil_desc.storage_mode = StorageMode::kDeviceTransient; if (enable_msaa) { depth_stencil_desc.type = TextureType::kTexture2DMultisample; +#ifdef OHOS_PLATFORM + depth_stencil_desc.sample_count = SampleCount::kCount2; +#else depth_stencil_desc.sample_count = SampleCount::kCount4; +#endif } else { depth_stencil_desc.type = TextureType::kTexture2D; depth_stencil_desc.sample_count = SampleCount::kCount1; @@ -324,8 +333,8 @@ bool KHRSwapchainImplVK::IsValid() const { void KHRSwapchainImplVK::WaitIdle() const { if (auto context = context_.lock()) { - [[maybe_unused]] auto result = - ContextVK::Cast(*context).GetDevice().waitIdle(); + // vkDeviceWaitIdle is equivalent to calling vkQueueWaitIdle on all queues. + ContextVK::Cast(*context).WaitIdle(); } } @@ -402,6 +411,54 @@ KHRSwapchainImplVK::AcquireResult KHRSwapchainImplVK::AcquireNextDrawable() { context.GetGPUTracer()->MarkFrameStart(); auto image = images_[index % images_.size()]; + + /// The GPU's write operations to the image must wait for the + /// sync->render_ready semaphore (i.e., wait for the GPU or hardware composer + /// to complete reading the image); + /// otherwise, screen tearing or other visual artifacts may occur. + /// However, the current function does not provide the render_ready semaphore + /// upon return, meaning subsequent write operations to the image will not + /// wait for the semaphore to signal, potentially leading to visual anomalies. + + /// To address this issue, a write barrier is added here for the image, + /// along with a wait for the corresponding semaphore, + /// ensuring correct rendering. + /// Note: vkWaitSemaphores might not function correctly when the semaphore is + /// imported from a sync FD. + sync->ready_cmd_buffer = context.CreateCommandBuffer(); + if (sync->ready_cmd_buffer) { + auto vk_cmd_buffer = CommandBufferVK::Cast(*sync->ready_cmd_buffer) + .GetEncoder() + ->GetCommandBuffer(); + BarrierVK barrier; + barrier.new_layout = vk::ImageLayout::eColorAttachmentOptimal; + barrier.cmd_buffer = vk_cmd_buffer; + barrier.src_access = {}; + barrier.src_stage = vk::PipelineStageFlagBits::eTopOfPipe; + barrier.dst_access = vk::AccessFlagBits::eColorAttachmentWrite; + barrier.dst_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput; + image->SetLayout(barrier); + + auto end_ret = vk_cmd_buffer.end(); + if (end_ret == vk::Result::eSuccess) { + vk::SubmitInfo submit_info; + vk::PipelineStageFlags wait_stage = + vk::PipelineStageFlagBits::eColorAttachmentOutput; + submit_info.setWaitDstStageMask(wait_stage); + submit_info.setWaitSemaphores(*sync->render_ready); + submit_info.setCommandBuffers(vk_cmd_buffer); + auto result = context.GetGraphicsQueue()->Submit(submit_info, nullptr); + if (result != vk::Result::eSuccess) { + VALIDATION_LOG << "Submit Swapchain Image Write Barrier Failed: " + << vk::to_string(result); + } + } else { + VALIDATION_LOG << "Command Buffer End Failed: " << vk::to_string(end_ret); + } + } else { + VALIDATION_LOG << "Create Command Buffer Failed"; + } + uint32_t image_index = index; return AcquireResult{KHRSurfaceVK::WrapSwapchainImage( context_strong, // context diff --git a/impeller/renderer/backend/vulkan/yuv_conversion_vk.h b/impeller/renderer/backend/vulkan/yuv_conversion_vk.h index 88c17ec6b9ad3729001c7fa2e7489bacb4be2cdf..706bef192dbc04bde4a5ec4495b6e815d8c73cac 100644 --- a/impeller/renderer/backend/vulkan/yuv_conversion_vk.h +++ b/impeller/renderer/backend/vulkan/yuv_conversion_vk.h @@ -27,6 +27,10 @@ using YUVConversionDescriptorVK = , vk::ExternalFormatANDROID #endif // FML_OS_ANDROID +#if FML_OS_OHOS + , + vk::ExternalFormatOHOS +#endif // FML_OS_OHOS >; class YUVConversionLibraryVK; diff --git a/impeller/renderer/command_queue.h b/impeller/renderer/command_queue.h index 140b1c396e244f03f635b9627af3fbc4f9cd223d..86697bf65f2d95b177a64485acab5517009740f4 100644 --- a/impeller/renderer/command_queue.h +++ b/impeller/renderer/command_queue.h @@ -12,6 +12,12 @@ namespace impeller { +#ifdef OHOS_PLATFORM +namespace vk { +class Semaphore; +} +#endif + /// @brief An interface for submitting command buffers to the GPU for /// encoding and execution. class CommandQueue { diff --git a/impeller/renderer/context.h b/impeller/renderer/context.h index 59ac871aad483f13ebe3140e0d2b9b770d19b3af..a6e62a12f84c2dfadf89ea4b7bb59b5cbe474967 100644 --- a/impeller/renderer/context.h +++ b/impeller/renderer/context.h @@ -194,6 +194,13 @@ class Context { /// shader variants, as well as forcing driver initialization. virtual void InitializeCommonlyUsedShadersIfNeeded() const {} + /// Dispose resources that are cached on behalf of the current thread. + /// + /// Some backends such as Vulkan may cache resources that can be reused while + /// executing a rendering operation. This API can be called after the + /// operation completes in order to clear the cache. + virtual void DisposeThreadLocalCachedResources() {} + protected: Context(); diff --git a/impeller/renderer/render_target.cc b/impeller/renderer/render_target.cc index 6afa486bab7bb0d155be54c41f202a45a6df588a..028be8a0a7570327e9eadc4a39eaf22d895d99b7 100644 --- a/impeller/renderer/render_target.cc +++ b/impeller/renderer/render_target.cc @@ -335,7 +335,11 @@ RenderTarget RenderTargetAllocator::CreateOffscreenMSAA( TextureDescriptor color0_tex_desc; color0_tex_desc.storage_mode = color_attachment_config.storage_mode; color0_tex_desc.type = TextureType::kTexture2DMultisample; +#ifdef OHOS_PLATFORM + color0_tex_desc.sample_count = SampleCount::kCount2; +#else color0_tex_desc.sample_count = SampleCount::kCount4; +#endif color0_tex_desc.format = pixel_format; color0_tex_desc.size = size; color0_tex_desc.usage = TextureUsage::kRenderTarget; @@ -427,7 +431,11 @@ void RenderTarget::SetupDepthStencilAttachments( stencil_attachment_config.storage_mode; if (msaa) { depth_stencil_texture_desc.type = TextureType::kTexture2DMultisample; +#ifdef OHOS_PLATFORM + depth_stencil_texture_desc.sample_count = SampleCount::kCount2; +#else depth_stencil_texture_desc.sample_count = SampleCount::kCount4; +#endif } depth_stencil_texture_desc.format = context.GetCapabilities()->GetDefaultDepthStencilFormat(); diff --git a/impeller/scene/scene_encoder.cc b/impeller/scene/scene_encoder.cc index a998ee00689a93166fc9403463ca46833b97c49f..b9edd3da43d93b49a0b47017e9ff8e9c6105b0b3 100644 --- a/impeller/scene/scene_encoder.cc +++ b/impeller/scene/scene_encoder.cc @@ -55,7 +55,11 @@ std::shared_ptr SceneEncoder::BuildSceneCommandBuffer( ds_texture.format = PixelFormat::kD32FloatS8UInt; ds_texture.size = render_target.GetRenderTargetSize(); ds_texture.usage = TextureUsage::kRenderTarget; +#ifdef OHOS_PLATFORM + ds_texture.sample_count = SampleCount::kCount2; +#else ds_texture.sample_count = SampleCount::kCount4; +#endif ds_texture.storage_mode = StorageMode::kDeviceTransient; auto texture = scene_context.GetContext()->GetResourceAllocator()->CreateTexture( diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 55453fe574f32cdd581418f7fcb473135cbf419d..c886f6501ff0137a4d503685b768f7a474a86887 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -18,12 +18,14 @@ declare_args() { impeller_enable_metal = (is_mac || is_ios) && target_os != "fuchsia" # Whether the OpenGLES backend is enabled. - impeller_enable_opengles = (is_linux || is_win || is_android || is_mac || - enable_unittests) && target_os != "fuchsia" + impeller_enable_opengles = + (is_linux || is_win || is_android || is_mac || is_ohos || + enable_unittests) && target_os != "fuchsia" # Whether the Vulkan backend is enabled. - impeller_enable_vulkan = (is_linux || is_win || is_android || is_mac || - enable_unittests) && target_os != "fuchsia" + impeller_enable_vulkan = + (is_linux || is_win || is_android || is_mac || is_ohos || + enable_unittests) && target_os != "fuchsia" # Whether to use a prebuilt impellerc. # If this is the empty string, impellerc will be built. @@ -326,9 +328,13 @@ template("impellerc") { iplr = invoker.iplr } json = false + remap_samplers = false if (defined(invoker.json) && invoker.json) { json = invoker.json } + if (defined(invoker.remap_samplers) && invoker.remap_samplers) { + remap_samplers = invoker.remap_samplers + } # Not needed on every path. not_needed([ @@ -338,6 +344,14 @@ template("impellerc") { "shader_bundle_output", ]) + # Optional: invoker.iplr Causes --sl output to be in iplr format. + # Optional: invoker.defines specifies a list of valueless macro definitions. + # Optional: invoker.intermediates_subdir specifies the subdirectory in which + # to put intermediates. + # Optional: invoker.json Causes output format to be JSON instead of flatbuffer. + # Optional: invoker.remap_samplers Output metal samplers according to + # declaration order instead of usage order. + _impellerc(target_name) { shader_bundle = defined(invoker.shader_bundle) @@ -393,6 +407,9 @@ template("impellerc") { if (json) { args += [ "--json" ] } + if (remap_samplers) { + args += [ "--remap-samplers" ] + } if (iplr) { # When building in IPLR mode, the compiler may be executed twice diff --git a/impeller/typographer/lazy_glyph_atlas.cc b/impeller/typographer/lazy_glyph_atlas.cc index 121e6186c9e090f6cc85c7e57d2f56b76ad769ba..28f3e47be9e67c9bfa32005ac3df296fb5a2d26f 100644 --- a/impeller/typographer/lazy_glyph_atlas.cc +++ b/impeller/typographer/lazy_glyph_atlas.cc @@ -86,6 +86,8 @@ const std::shared_ptr& LazyGlyphAtlas::CreateOrGetGlyphAtlas( return color_atlas_; } FML_UNREACHABLE(); + static std::shared_ptr null_atlas(nullptr); + return null_atlas; } } // namespace impeller diff --git a/lib/gpu/formats.h b/lib/gpu/formats.h index 8c703032adf30d0f2cd60db525d2f319f465a60e..7a9135d8bf57e70d2f3faf7781693c9f1fbd7dc2 100644 --- a/lib/gpu/formats.h +++ b/lib/gpu/formats.h @@ -317,6 +317,7 @@ constexpr FlutterGPUShaderStage FromImpellerShaderStage( << static_cast(value); FML_UNREACHABLE(); } + return FlutterGPUShaderStage::kVertex; } enum class FlutterGPUMinMagFilter { diff --git a/lib/gpu/texture.cc b/lib/gpu/texture.cc index 740e3dc51ae4410278b98ca9a580ecfc666fc750..2f2131d77ff3e64882b3f0f5eda3478fed3dc5b6 100644 --- a/lib/gpu/texture.cc +++ b/lib/gpu/texture.cc @@ -101,6 +101,10 @@ bool InternalFlutterGpu_Texture_Initialize(Dart_Handle wrapper, desc.type = impeller::TextureType::kTexture2D; desc.sample_count = impeller::SampleCount::kCount1; break; + case 2: + desc.type = impeller::TextureType::kTexture2DMultisample; + desc.sample_count = impeller::SampleCount::kCount2; + break; case 4: desc.type = impeller::TextureType::kTexture2DMultisample; desc.sample_count = impeller::SampleCount::kCount4; diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index 485d36b6bdf340c637a1a2ff186c650e38754e8b..b8889bfcbcd49b9870faa77c1e94d49704c7d8ef 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -14,6 +14,8 @@ #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/window/platform_configuration.h" +#include "flutter/fml/logging.h" + using tonic::ToDart; namespace flutter { @@ -238,7 +240,7 @@ void Canvas::drawLine(double x1, void Canvas::drawPaint(Dart_Handle paint_objects, Dart_Handle paint_data) { Paint paint(paint_objects, paint_data); - + FML_DLOG(INFO)<<"drawPaint:" <<(int64_t)paint_objects ; FML_DCHECK(paint.isNotNull()); if (display_list_builder_) { DlPaint dl_paint; diff --git a/lib/ui/painting/image_decoder_impeller.cc b/lib/ui/painting/image_decoder_impeller.cc index 587960f2baf3c5ef907b8dcc295b74e9ed08321c..395f20402d1ef2764d062d270f893d921f8b3a68 100644 --- a/lib/ui/painting/image_decoder_impeller.cc +++ b/lib/ui/painting/image_decoder_impeller.cc @@ -346,6 +346,8 @@ static std::pair, std::string> UnsafeUploadTextureToPrivate( return std::make_pair(nullptr, decode_error); } + context->DisposeThreadLocalCachedResources(); + return std::make_pair( impeller::DlImageImpeller::Make(std::move(dest_texture)), std::string()); } @@ -470,6 +472,7 @@ ImageDecoderImpeller::UploadTextureToStorage( return std::make_pair(nullptr, decode_error.value()); } } + context->DisposeThreadLocalCachedResources(); return std::make_pair(impeller::DlImageImpeller::Make(std::move(texture)), std::string()); diff --git a/lib/ui/painting/image_decoder_unittests.cc b/lib/ui/painting/image_decoder_unittests.cc index 2504a95064b6e81edaa467c21e75a06ddec3fda0..475e176cd8b243de513184591a8b6aa20ac8d094 100644 --- a/lib/ui/painting/image_decoder_unittests.cc +++ b/lib/ui/painting/image_decoder_unittests.cc @@ -69,6 +69,7 @@ class TestImpellerContext : public impeller::Context { std::shared_ptr GetCommandQueue() const override { FML_UNREACHABLE(); + return std::make_shared(); } std::shared_ptr CreateCommandBuffer() const override { @@ -78,10 +79,15 @@ class TestImpellerContext : public impeller::Context { void Shutdown() override {} + void DisposeThreadLocalCachedResources() override { did_dispose_ = true; } + + bool DidDisposeResources() const { return did_dispose_; } + mutable size_t command_buffer_count_ = 0; private: std::shared_ptr capabilities_; + bool did_dispose_ = false; }; } // namespace impeller @@ -340,12 +346,14 @@ TEST_F(ImageDecoderFixtureTest, ImpellerUploadToSharedNoGpu) { no_gpu_access_context, buffer, info, bitmap, gpu_disabled_switch); ASSERT_EQ(no_gpu_access_context->command_buffer_count_, 0ul); ASSERT_EQ(result.second, ""); + EXPECT_EQ(no_gpu_access_context->DidDisposeResources(), false); result = ImageDecoderImpeller::UploadTextureToStorage( no_gpu_access_context, bitmap, gpu_disabled_switch, impeller::StorageMode::kHostVisible, true); ASSERT_EQ(no_gpu_access_context->command_buffer_count_, 0ul); ASSERT_EQ(result.second, ""); + EXPECT_EQ(no_gpu_access_context->DidDisposeResources(), true); } TEST_F(ImageDecoderFixtureTest, ImpellerNullColorspace) { diff --git a/lib/ui/painting/image_encoding_impeller.cc b/lib/ui/painting/image_encoding_impeller.cc index 6eb3c201d2944b983c91ff3c529b6e9119c12558..7c1a9800b5a9a9ceb6e1042ed62733fe2f8ba179 100644 --- a/lib/ui/painting/image_encoding_impeller.cc +++ b/lib/ui/painting/image_encoding_impeller.cc @@ -182,6 +182,8 @@ void ImageEncodingImpeller::ConvertDlImageToSkImage( .ok()) { FML_LOG(ERROR) << "Failed to submit commands."; } + + impeller_context->DisposeThreadLocalCachedResources(); } void ImageEncodingImpeller::ConvertImageToRaster( diff --git a/lib/ui/painting/image_generator_registry.cc b/lib/ui/painting/image_generator_registry.cc index ca5d83a36d359e0f2adc33ad372dd89f61a4eed4..c6a877a3550bc450c9b820164abf27fecfcb0d29 100644 --- a/lib/ui/painting/image_generator_registry.cc +++ b/lib/ui/painting/image_generator_registry.cc @@ -23,8 +23,13 @@ ImageGeneratorRegistry::ImageGeneratorRegistry() : weak_factory_(this) { [](sk_sp buffer) { return APNGImageGenerator::MakeFromData(std::move(buffer)); }, +#ifdef OHOS_PLATFORM + // OHOS's PixelMap currently does not support APNG decoding. Direct + // decoding will result in the image being decoded as PNG. + 2); +#else 0); - +#endif AddFactory( [](sk_sp buffer) { return BuiltinSkiaCodecImageGenerator::MakeFromData(std::move(buffer)); diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 63d7211dccba25af9ae9cf4e9a3728f82de86732..1bbba16a8576769a6636d21ef2abf2f75b6b2d52 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -416,6 +416,7 @@ class PlatformDispatcher { // Called from the engine, via hooks.dart void _drawFrame() { + // print("platform_dispatcher::drawFrame..${_onDrawFrame}") ; _invoke(onDrawFrame, _onDrawFrameZone); } diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 574b9e27f33ca1a9ab319e5cb503d7b101463db3..965ca85f4f6d2a1d78632822dd9d8128a403fb18 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -7,6 +7,7 @@ #include #include "flutter/common/constants.h" +#include "flutter/fml/trace_event.h" #include "flutter/lib/ui/compositing/scene.h" #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/window/platform_message.h" @@ -20,6 +21,8 @@ #include "third_party/tonic/logging/dart_invoke.h" #include "third_party/tonic/typed_data/dart_byte_data.h" +#include "flutter/fml/platform/ohos/hisysevent_c.h" + namespace flutter { namespace { @@ -293,6 +296,7 @@ void PlatformConfiguration::DispatchPlatformMessage( std::unique_ptr message) { std::shared_ptr dart_state = dispatch_platform_message_.dart_state().lock(); + if (!dart_state) { FML_DLOG(WARNING) << "Dropping platform message for lack of DartState on channel: " @@ -357,7 +361,6 @@ void PlatformConfiguration::DispatchSemanticsAction(int32_t node_id, if (Dart_IsError(args_handle)) { return; } - tonic::CheckAndHandleError(tonic::DartInvoke( dispatch_semantics_action_.Get(), {tonic::ToDart(node_id), tonic::ToDart(static_cast(action)), @@ -366,6 +369,7 @@ void PlatformConfiguration::DispatchSemanticsAction(int32_t node_id, void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime, uint64_t frame_number) { + TRACE_EVENT0("flutter", "PlatformConfiguration::BeginFrame"); std::shared_ptr dart_state = begin_frame_.dart_state().lock(); if (!dart_state) { @@ -375,6 +379,7 @@ void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime, int64_t microseconds = (frameTime - fml::TimePoint()).ToMicroseconds(); + TRACE_EVENT0("flutter", "PlatformConfiguration::begin_frame_"); tonic::CheckAndHandleError( tonic::DartInvoke(begin_frame_.Get(), { Dart_NewInteger(microseconds), @@ -383,6 +388,7 @@ void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime, UIDartState::Current()->FlushMicrotasksNow(); + TRACE_EVENT0("flutter", "PlatformConfiguration::draw_frame_"); tonic::CheckAndHandleError(tonic::DartInvokeVoid(draw_frame_.Get())); } diff --git a/lib/ui/window/platform_message_response_dart.cc b/lib/ui/window/platform_message_response_dart.cc index 66c07c31c36ed2c7063542484b9a34dee1665213..3f43e20451884d21a0f6539102b804083e00f88c 100644 --- a/lib/ui/window/platform_message_response_dart.cc +++ b/lib/ui/window/platform_message_response_dart.cc @@ -39,8 +39,9 @@ void PostCompletion(Callback&& callback, ui_task_runner->PostTask(fml::MakeCopyable( [callback = std::move(callback), platform_message_id, result = std::move(result), channel = channel]() mutable { - TRACE_EVENT_ASYNC_END0("flutter", "PlatformChannel ScheduleResult", - platform_message_id); + // Pair with TRACE_EVENT_ASYNC_BEGIN1 + TRACE_EVENT_ASYNC_END1("flutter", "PlatformChannel ScheduleResult", + platform_message_id, "channel", channel.c_str()); std::shared_ptr dart_state = callback.dart_state().lock(); if (!dart_state) { diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index 1ed968a4d62d4459a07b92ff2a36199a662367d5..d591070c12fb3ff63d45717c06971e57314e9051 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -89,7 +89,7 @@ source_set("runtime") { "skia_concurrent_executor.h", ] - if (is_ios && flutter_runtime_mode == "debug") { + if ((is_ios || is_ohos) && flutter_runtime_mode == "debug") { # These contain references to private APIs and this TU must only be compiled in debug runtime modes. sources += [ "ptrace_check.cc" ] } diff --git a/runtime/dart_snapshot.cc b/runtime/dart_snapshot.cc index 5943524aa9f8066ae8738b028f1c42c595bb6200..cb92670b8997b59ec6ab77cd2bda3413e07ed5cb 100644 --- a/runtime/dart_snapshot.cc +++ b/runtime/dart_snapshot.cc @@ -9,6 +9,7 @@ #include "flutter/fml/native_library.h" #include "flutter/fml/paths.h" #include "flutter/fml/trace_event.h" +#include "flutter/fml/logging.h" #include "flutter/lib/snapshot/snapshot.h" #include "flutter/runtime/dart_vm.h" #include "third_party/dart/runtime/include/dart_api.h" @@ -56,6 +57,7 @@ static std::shared_ptr SearchMapping( const std::vector& native_library_path, const char* native_library_symbol_name, bool is_executable) { + FML_LOG(INFO)<<"SearchMapping file:"< SearchMapping( static std::shared_ptr ResolveVMData( const Settings& settings) { #if DART_SNAPSHOT_STATIC_LINK + FML_LOG(ERROR)<<"ResolveVMData: NonOwnedMapping" ; return std::make_unique(kDartVmSnapshotData, 0, // size nullptr, // release_func diff --git a/runtime/dart_vm.cc b/runtime/dart_vm.cc index 23e59cf93b0f2231cdbce90aee7b28b144fd3604..0930468c541d0510e8695b3f1056ac1a0a49b59e 100644 --- a/runtime/dart_vm.cc +++ b/runtime/dart_vm.cc @@ -346,7 +346,7 @@ DartVM::DartVM(const std::shared_ptr& vm_data, #endif // !OS_FUCHSIA #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) -#if !FML_OS_IOS && !FML_OS_MACOSX +#if !FML_OS_IOS && !FML_OS_MACOSX && !FML_OS_OHOS // Debug mode uses the JIT, disable code page write protection to avoid // memory page protection changes before and after every compilation. PushBackAll(&args, kDartWriteProtectCodeArgs, diff --git a/runtime/dart_vm_initializer.cc b/runtime/dart_vm_initializer.cc index fbd941d11f8ce368c19773b6d83c9c37e48d712c..0a41a02beb52c60e634ffbe60338248a9eaf4424 100644 --- a/runtime/dart_vm_initializer.cc +++ b/runtime/dart_vm_initializer.cc @@ -38,8 +38,8 @@ void LogUnhandledException(Dart_Handle exception_handle, // Either the exception handler was not set or it could not handle the // error, just log the exception. - FML_LOG(ERROR) << "Unhandled Exception: " << error << std::endl - << stack_trace; + // FML_LOG(ERROR) << "Unhandled Exception: " << error << std::endl + // << stack_trace; } void ReportUnhandledException(Dart_Handle exception_handle, diff --git a/runtime/ptrace_check.cc b/runtime/ptrace_check.cc index c9baa6d9c5b1ea3e58bcf3b95b63e178cfba0ec5..c0ed2a79c2fc292e447a080f4323c1e489d43631 100644 --- a/runtime/ptrace_check.cc +++ b/runtime/ptrace_check.cc @@ -24,6 +24,7 @@ #if TRACING_CHECKS_NECESSARY +#ifdef FML_OS_IOS #include #include @@ -145,4 +146,58 @@ TracingResult GetTracingResultImpl() { } // namespace flutter +#endif // FML_OS_IOS + + +#ifdef FML_OS_OHOS + +#include +#include + +#include + +#include "flutter/fml/build_config.h" + +// Being extra careful and adding additional landmines that will prevent +// compilation of this TU in an incorrect runtime mode. +static_assert(FML_OS_OHOS, "This translation unit is ohos specific."); +static_assert(FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG, + "This translation unit must only be compiled in the debug " + "runtime mode as it " + "contains private API usage."); + +namespace flutter { + +static bool IsLaunchedByFlutterCLI(const Settings& vm_settings) { + return vm_settings.enable_checked_mode; +} + +static bool EnableTracingIfNecessaryOnce(const Settings& vm_settings) { + if (IsLaunchedByFlutterCLI(vm_settings)) { + return true; + } + return false; +} + +static TracingResult sTracingResult = TracingResult::kNotAttempted; + +bool EnableTracingIfNecessaryImpl(const Settings& vm_settings) { + static std::once_flag tracing_flag; + + std::call_once(tracing_flag, [&vm_settings]() { + sTracingResult = EnableTracingIfNecessaryOnce(vm_settings) + ? TracingResult::kEnabled + : TracingResult::kDisabled; + }); + return sTracingResult != TracingResult::kDisabled; +} + +TracingResult GetTracingResultImpl() { + return sTracingResult; +} + +} // namespace flutter + +#endif // FML_OS_OHOS + #endif // TRACING_CHECKS_NECESSARY diff --git a/runtime/ptrace_check.h b/runtime/ptrace_check.h index 1c023bdb333de94cb7d78f59e11de70aac39024c..a497d3c7bfa32cce5f2d8cd0b5d0ceaf8e5f3ec4 100644 --- a/runtime/ptrace_check.h +++ b/runtime/ptrace_check.h @@ -11,7 +11,7 @@ namespace flutter { #define TRACING_CHECKS_NECESSARY \ - FML_OS_IOS && !TARGET_OS_SIMULATOR && \ + (FML_OS_IOS || FML_OS_OHOS) && !TARGET_OS_SIMULATOR && \ (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) enum class TracingResult { diff --git a/shell/common/animator.cc b/shell/common/animator.cc index 94a037c71f3705b50fd1a97e379e99e004cad480..0555fcf4dfca86f1118a28d4a8e04ef86d985aec 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -6,6 +6,7 @@ #include "flutter/common/constants.h" #include "flutter/flow/frame_timings.h" +#include "flutter/fml/logging.h" #include "flutter/fml/time/time_point.h" #include "flutter/fml/trace_event.h" #include "third_party/dart/runtime/include/dart_tools_api.h" @@ -237,6 +238,7 @@ void Animator::DrawLastLayerTrees( } void Animator::RequestFrame(bool regenerate_layer_trees) { + TRACE_EVENT0("flutter", "Animator::RequestFrame"); if (regenerate_layer_trees) { // This event will be closed by BeginFrame. BeginFrame will only be called // if regenerating the layer trees. If a frame has been requested to update @@ -250,6 +252,7 @@ void Animator::RequestFrame(bool regenerate_layer_trees) { if (!pending_frame_semaphore_.TryWait()) { // Multiple calls to Animator::RequestFrame will still result in a // single request to the VsyncWaiter. + TRACE_EVENT0("flutter", "Animator::pending_frame_semaphore"); return; } @@ -271,6 +274,7 @@ void Animator::RequestFrame(bool regenerate_layer_trees) { } void Animator::AwaitVSync() { + TRACE_EVENT0("flutter", "Animator::AwaitVSync"); waiter_->AsyncWaitForVsync( [self = weak_factory_.GetWeakPtr()]( std::unique_ptr frame_timings_recorder) { diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 7dd03ed19fb78dff4919f2c7d24860a90f35717d..c4081ad891e694e52d01725b7f6efe8c2f8aee55 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -472,11 +472,17 @@ void Engine::Render(int64_t view_id, std::unique_ptr layer_tree, float device_pixel_ratio) { if (!layer_tree) { + FML_DLOG(ERROR) << "Render layer_tree IS NULL"; return; } + if (layer_tree->frame_size().isEmpty()) { + FML_DLOG(INFO) << "engin Render frame_size is empty"; + } + // Ensure frame dimensions are sane. if (layer_tree->frame_size().isEmpty() || device_pixel_ratio <= 0.0f) { + FML_DLOG(INFO) << "engin Render device_pixel_ratio <= 0"; return; } diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 48439c252447a1883492290e88708eac2068e181..e7043a86c2bafbfd4db9323d620492e6684c4811 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -14,7 +14,9 @@ namespace flutter { PlatformView::PlatformView(Delegate& delegate, const TaskRunners& task_runners) - : delegate_(delegate), task_runners_(task_runners), weak_factory_(this) {} + : delegate_(delegate), task_runners_(task_runners), weak_factory_(this) { + FML_LOG(INFO) << "PlatformView"; +} PlatformView::~PlatformView() = default; diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 060d928f10bcaaef137845548dc488f530acfaee..b99d8148f6826a0b6c43da0518fc30756f130fed 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -43,6 +43,9 @@ #include "impeller/display_list/dl_dispatcher.h" // nogncheck #endif +#include "flutter/fml/logging.h" +#include "flutter/fml/platform/ohos/hisysevent_c.h" + namespace flutter { // The rasterizer will tell Skia to purge cached resources that have not been @@ -235,6 +238,7 @@ void Rasterizer::DrawLastLayerTrees( } DrawStatus Rasterizer::Draw(const std::shared_ptr& pipeline) { + // HISYSEVENT_WRITE_DURATION("flutter rasterize frame time"); TRACE_EVENT0("flutter", "GPURasterizer::Draw"); if (raster_thread_merger_ && !raster_thread_merger_->IsOnRasterizingThread()) { diff --git a/shell/common/shell.cc b/shell/common/shell.cc index e53ca5f3e3ad08b3dcd7a3454f070e69ce2d0205..5c0d22f37cf126858d9575b678aa2524a1479c1e 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #define RAPIDJSON_HAS_STDSTRING 1 #include "flutter/shell/common/shell.h" @@ -107,10 +108,9 @@ void PerformInitializationTasks(Settings& settings) { { fml::LogSettings log_settings; log_settings.min_log_level = - settings.verbose_logging ? fml::kLogInfo : fml::kLogError; + settings.verbose_logging ? fml::kLogInfo : fml::kLogWarning; fml::SetLogSettings(log_settings); } - static std::once_flag gShellSettingsInitialization = {}; std::call_once(gShellSettingsInitialization, [&settings] { tonic::SetLogHandler( @@ -227,6 +227,7 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( FML_LOG(ERROR) << "Task runners to run the shell were invalid."; return nullptr; } + FML_LOG(INFO) << "CreateShellOnPlatformThread"; auto shell = std::unique_ptr( new Shell(std::move(vm), task_runners, std::move(parent_merger), @@ -358,12 +359,14 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( runtime_stage_backend // )); })); + FML_LOG(INFO) << "CreateShellOnPlatformThread Setup"; if (!shell->Setup(std::move(platform_view), // engine_future.get(), // rasterizer_future.get(), // io_manager_future.get()) // ) { + FML_LOG(ERROR) << "CreateShellOnPlatformThread Setup nullptr"; return nullptr; } @@ -598,6 +601,7 @@ std::unique_ptr Shell::Spawn( fml::SyncSwitch::Handlers() .SetIfFalse([&is_gpu_disabled] { is_gpu_disabled = false; }) .SetIfTrue([&is_gpu_disabled] { is_gpu_disabled = true; })); + std::unique_ptr result = CreateWithSnapshot( PlatformData{}, task_runners_, rasterizer_->GetRasterThreadMerger(), io_manager_, resource_cache_limit_calculator_, GetSettings(), vm_, diff --git a/shell/common/snapshot_controller_impeller.cc b/shell/common/snapshot_controller_impeller.cc index ea68bd91188a4ad1884b500ca4ce83cbdff141eb..c855c1c5f5a8714e973233958c7918fbbebb1181 100644 --- a/shell/common/snapshot_controller_impeller.cc +++ b/shell/common/snapshot_controller_impeller.cc @@ -73,6 +73,8 @@ sk_sp SnapshotControllerImpeller::DoMakeRasterSnapshot( sk_sp SnapshotControllerImpeller::ConvertToRasterImage( sk_sp image) { FML_UNREACHABLE(); + sk_sp result; + return result; } } // namespace flutter diff --git a/shell/common/switches.cc b/shell/common/switches.cc index fd3d9508fb8c98364027b42dd927f932bf596d67..261b58dad9043bdf37cc21065d5b30fb09f42c6b 100644 --- a/shell/common/switches.cc +++ b/shell/common/switches.cc @@ -76,7 +76,7 @@ static const std::string kAllowedDartFlags[] = { // Define symbols for the ICU data that is linked into the Flutter library on // Android. This is a workaround for crashes seen when doing dynamic lookups // of the engine's own symbols on some older versions of Android. -#if FML_OS_ANDROID +#if FML_OS_ANDROID || FML_OS_OHOS extern uint8_t _binary_icudtl_dat_start[]; extern size_t _binary_icudtl_dat_size; @@ -430,7 +430,7 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { command_line.GetOptionValue(FlagForSwitch(Switch::ICUNativeLibPath), &native_lib_path); -#if FML_OS_ANDROID +#if FML_OS_ANDROID || FML_OS_OHOS settings.icu_mapper = GetICUStaticMapping; #else settings.icu_mapper = [icu_symbol_prefix, native_lib_path] { diff --git a/shell/common/vsync_waiter.cc b/shell/common/vsync_waiter.cc index 80074fa3ef9826331c77c2e6b27febed7eca6618..d1d14d8f573a78ea01491ba6ae3bca27785b7ca2 100644 --- a/shell/common/vsync_waiter.cc +++ b/shell/common/vsync_waiter.cc @@ -14,9 +14,9 @@ namespace flutter { -static constexpr const char* kVsyncFlowName = "VsyncFlow"; +const char* kVsyncFlowName = "VsyncFlow"; -static constexpr const char* kVsyncTraceName = "VsyncProcessCallback"; +const char* kVsyncTraceName = "VsyncProcessCallback"; VsyncWaiter::VsyncWaiter(const TaskRunners& task_runners) : task_runners_(task_runners) {} diff --git a/shell/common/vsync_waiter.h b/shell/common/vsync_waiter.h index fa644f63bb0ac896b7b26937165269dea4225922..4ae8ea5d71f8231d17deabdf5337a3c91d6ecf48 100644 --- a/shell/common/vsync_waiter.h +++ b/shell/common/vsync_waiter.h @@ -40,6 +40,7 @@ class VsyncWaiter : public std::enable_shared_from_this { // method. friend class VsyncWaiterAndroid; friend class VsyncWaiterEmbedder; + friend class VsyncWaiterOHOS; const TaskRunners task_runners_; diff --git a/shell/config.gni b/shell/config.gni index 3f498d2c044f7c4a46a89a8ab546cb0c78e24888..faf85d4f0b435df15870d0b011697552b6129957 100644 --- a/shell/config.gni +++ b/shell/config.gni @@ -3,7 +3,7 @@ # found in the LICENSE file. declare_args() { - shell_enable_gl = !is_fuchsia && !is_mac + shell_enable_gl = !is_fuchsia && !is_mac && !is_ohos # The logic for enabling Vulkan and Metal is in tools/gn. shell_enable_metal = false diff --git a/shell/gpu/gpu_surface_software.cc b/shell/gpu/gpu_surface_software.cc index efb905ba24ecb52662437962ffd76912ce15a15a..8b90ea240bc0d4c5bf6b5535fc83a08f0195ccda 100644 --- a/shell/gpu/gpu_surface_software.cc +++ b/shell/gpu/gpu_surface_software.cc @@ -31,6 +31,7 @@ std::unique_ptr GPUSurfaceSoftware::AcquireFrame( SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; + FML_DLOG(INFO) <<"AcquireFrame" ; // TODO(38466): Refactor GPU surface APIs take into account the fact that an // external view embedder may want to render to the root surface. if (!render_to_surface_) { @@ -50,11 +51,14 @@ std::unique_ptr GPUSurfaceSoftware::AcquireFrame( sk_sp backing_store = delegate_->AcquireBackingStore(size); +FML_DLOG(INFO) << "AcquireFrame"; if (backing_store == nullptr) { + FML_DLOG(INFO) <<"AcquireFrame ... backing_store is null" ; return nullptr; } if (size != SkISize::Make(backing_store->width(), backing_store->height())) { + FML_DLOG(INFO) <<"AcquireFrame ... backing_store size not matched" ; return nullptr; } @@ -64,19 +68,25 @@ std::unique_ptr GPUSurfaceSoftware::AcquireFrame( SkCanvas* canvas = backing_store->getCanvas(); canvas->resetMatrix(); +FML_DLOG(INFO) << "resetMatrix end"; SurfaceFrame::SubmitCallback on_submit = [self = weak_factory_.GetWeakPtr()](const SurfaceFrame& surface_frame, DlCanvas* canvas) -> bool { // If the surface itself went away, there is nothing more to do. + FML_DLOG(INFO) <<"AcquireFrame ... on_submit ..." ; if (!self || !self->IsValid() || canvas == nullptr) { + FML_DLOG(INFO) << "on_submit return false"; return false; } + FML_DLOG(INFO) <<"AcquireFrame ... on_submit canvas->flush " ; canvas->Flush(); + FML_DLOG(INFO) <<"AcquireFrame ... delegate_-->PresentBackingStore" ; return self->delegate_->PresentBackingStore(surface_frame.SkiaSurface()); }; +FML_DLOG(INFO) << "return SurfaceFrame"; return std::make_unique(backing_store, framebuffer_info, on_submit, logical_size); } diff --git a/shell/gpu/gpu_surface_vulkan_delegate.h b/shell/gpu/gpu_surface_vulkan_delegate.h index 2a95f0a613b51db87eb67fa1648d055bcb04d275..bead3284f6a59e0252c11b93441e0e49f524d431 100644 --- a/shell/gpu/gpu_surface_vulkan_delegate.h +++ b/shell/gpu/gpu_surface_vulkan_delegate.h @@ -10,10 +10,27 @@ #include "flutter/vulkan/procs/vulkan_proc_table.h" #include "flutter/vulkan/vulkan_device.h" #include "flutter/vulkan/vulkan_image.h" +#include "fml/time/time_point.h" +#include "include/core/SkRect.h" #include "third_party/skia/include/core/SkSize.h" namespace flutter { +// Information passed during presentation of a frame. +struct VulkanPresentInfo { + // The frame damage is a hint to compositor telling it which parts of front + // buffer need to be updated. + const std::optional& frame_damage; + + // Time at which this frame is scheduled to be presented. This is a hint + // that can be passed to the platform to drop queued frames. + std::optional presentation_time = std::nullopt; + + // The buffer damage refers to the region that needs to be set as damaged + // within the frame buffer. + const std::optional& buffer_damage; +}; + //------------------------------------------------------------------------------ /// @brief Interface implemented by all platform surfaces that can present /// a Vulkan backing store to the "screen". The GPU surface @@ -43,6 +60,12 @@ class GPUSurfaceVulkanDelegate { /// and it's ready to be bound for further reading/writing. /// virtual bool PresentImage(VkImage image, VkFormat format) = 0; + + /// @brief Called by the engine to tell the delegate present_info. + /// + virtual bool SetPresentInfo(const VulkanPresentInfo& present_info) { + return false; + }; }; } // namespace flutter diff --git a/shell/gpu/gpu_surface_vulkan_impeller.cc b/shell/gpu/gpu_surface_vulkan_impeller.cc index cf2733baab5ea6eeab314fb2d039b3d954b4d603..92090e6218e2393f4f433bb1e4ddc81de729c790 100644 --- a/shell/gpu/gpu_surface_vulkan_impeller.cc +++ b/shell/gpu/gpu_surface_vulkan_impeller.cc @@ -68,7 +68,8 @@ std::unique_ptr GPUSurfaceVulkanImpeller::AcquireFrame( SurfaceFrame::SubmitCallback submit_callback = fml::MakeCopyable([renderer = impeller_renderer_, // aiks_context = aiks_context_, // - surface = std::move(surface) // + surface = std::move(surface), // + delegate = delegate_ // ](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool { if (!aiks_context) { return false; @@ -80,6 +81,16 @@ std::unique_ptr GPUSurfaceVulkanImpeller::AcquireFrame( return false; } + if (delegate) { + VulkanPresentInfo present_info = { + .frame_damage = surface_frame.submit_info().frame_damage, + .presentation_time = + surface_frame.submit_info().presentation_time, + .buffer_damage = surface_frame.submit_info().buffer_damage, + }; + delegate->SetPresentInfo(present_info); + } + auto cull_rect = surface->GetTargetRenderPassDescriptor().GetRenderTargetSize(); impeller::Rect dl_cull_rect = impeller::Rect::MakeSize(cull_rect); diff --git a/shell/gpu/gpu_surface_vulkan_impeller.h b/shell/gpu/gpu_surface_vulkan_impeller.h index 3ddb106fa7c49d9fa8db45421f33366695065875..9d82d5d527a56f6d5d1c0a8df5b0e80d6c80a282 100644 --- a/shell/gpu/gpu_surface_vulkan_impeller.h +++ b/shell/gpu/gpu_surface_vulkan_impeller.h @@ -25,12 +25,18 @@ class GPUSurfaceVulkanImpeller final : public Surface { // |Surface| bool IsValid() override; + void SetDelegate(GPUSurfaceVulkanDelegate* delegate) { + this->delegate_ = delegate; + }; + private: std::shared_ptr impeller_context_; std::shared_ptr impeller_renderer_; std::shared_ptr aiks_context_; bool is_valid_ = false; + GPUSurfaceVulkanDelegate* delegate_ = nullptr; + // |Surface| std::unique_ptr AcquireFrame(const SkISize& size) override; diff --git a/shell/platform/BUILD.gn b/shell/platform/BUILD.gn index 63e9901643614de3de5711d7adbad33d5e3e2d9d..828cf8d84db884e0662f82e34846d1ffd6f423b3 100644 --- a/shell/platform/BUILD.gn +++ b/shell/platform/BUILD.gn @@ -9,6 +9,8 @@ group("platform") { deps = [ "darwin" ] } else if (is_android) { deps = [ "android" ] + }else if ( is_ohos){ + deps = [ "ohos" ] } else if (is_linux) { deps = [] if (enable_desktop_embeddings) { diff --git a/shell/platform/android/android_context_gl_unittests.cc b/shell/platform/android/android_context_gl_unittests.cc index 0bc2848f292b4c7e646891f09694bae3fe92b34f..df925f73699a3ad807729042b39bd68a45784144 100644 --- a/shell/platform/android/android_context_gl_unittests.cc +++ b/shell/platform/android/android_context_gl_unittests.cc @@ -50,36 +50,46 @@ class TestImpellerContext : public impeller::Context { const std::shared_ptr& GetCapabilities() const override { FML_UNREACHABLE(); + static const std::shared_ptr empty_ptr = + nullptr; + return empty_ptr; } bool UpdateOffscreenLayerPixelFormat(impeller::PixelFormat format) override { FML_UNREACHABLE(); + return false; } std::shared_ptr GetResourceAllocator() const override { FML_UNREACHABLE(); + return nullptr; } std::shared_ptr GetShaderLibrary() const override { FML_UNREACHABLE(); + return nullptr; } std::shared_ptr GetSamplerLibrary() const override { FML_UNREACHABLE(); + return nullptr; } std::shared_ptr GetPipelineLibrary() const override { FML_UNREACHABLE(); + return nullptr; } std::shared_ptr CreateCommandBuffer() const override { FML_UNREACHABLE(); + return nullptr; } std::shared_ptr GetCommandQueue() const override { FML_UNREACHABLE(); + return nullptr; } void Shutdown() override { did_shutdown = true; } diff --git a/shell/platform/android/android_surface_gl_impeller.cc b/shell/platform/android/android_surface_gl_impeller.cc index b2d0b69d9a9cfb0d51c8e4ed5b719d48fb85541c..df5b0a32a0364e3a44c2457bbfda52094217d232 100644 --- a/shell/platform/android/android_surface_gl_impeller.cc +++ b/shell/platform/android/android_surface_gl_impeller.cc @@ -8,6 +8,10 @@ #include "flutter/impeller/toolkit/egl/surface.h" #include "flutter/shell/gpu/gpu_surface_gl_impeller.h" +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wreturn-type" +#endif + namespace flutter { AndroidSurfaceGLImpeller::AndroidSurfaceGLImpeller( diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index ba7d1276f54a7c21094b105338d71335d850253a..bde7ecacce2dea5e00b7ff98ebbc076e62788de1 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -181,6 +181,9 @@ template("embedder_source_set") { if (is_android) { deps += [ "//flutter/shell/platform/android:icudtl_asm" ] sources += [ "$root_build_dir/gen/flutter/shell/platform/android/flutter/third_party/icu/flutter/icudtl.dat.S" ] + } else if (is_ohos) { + deps += [ "//flutter/shell/platform/ohos:icudtl_asm" ] + sources += [ "$root_build_dir/gen/flutter/shell/platform/ohos/flutter/third_party/icu/flutter/icudtl.dat.S" ] } public_configs += [ @@ -431,7 +434,7 @@ shared_library("flutter_engine_library") { if (is_mac && !embedder_for_target) { ldflags += [ "-Wl,-install_name,@rpath/FlutterEmbedder.framework/$_framework_binary_subpath" ] } - if (is_linux) { + if (is_ohos || is_linux) { ldflags += [ "-Wl,--version-script=" + rebase_path("embedder_exports.lst") ] } diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 52e49646c009a6a75df1c88b53a05914e5de78f1..af8e259c83a03e3d5dc6a6458119b43338cefa25 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -99,6 +99,24 @@ extern const intptr_t kPlatformStrongDillSize; #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h" #endif // SHELL_ENABLE_VULKAN +#ifdef FML_OS_OHOS +extern "C"{ + typedef enum { + /** Debug level to be used by {@link OH_LOG_DEBUG} */ + HILOG_LOG_DEBUG = 3, + /** Informational level to be used by {@link OH_LOG_INFO} */ + HILOG_LOG_INFO = 4, + /** Warning level to be used by {@link OH_LOG_WARN} */ + HILOG_LOG_WARN = 5, + /** Error level to be used by {@link OH_LOG_ERROR} */ + HILOG_LOG_ERROR = 6, + /** Fatal level to be used by {@link OH_LOG_FATAL} */ + HILOG_LOG_FATAL = 7, +} HiLog_LogLevel; +int OH_LOG_Print(int type, HiLog_LogLevel level, unsigned int domain, const char *tag, const char *fmt, ...) ; +} +#endif + const int32_t kFlutterSemanticsNodeIdBatchEnd = -1; const int32_t kFlutterSemanticsCustomActionIdBatchEnd = -1; @@ -144,6 +162,15 @@ static FlutterEngineResult LogEmbedderError(FlutterEngineResult code, "%s (%d): '%s' returned '%s'. %s", file_base, line, function, code_name, reason); std::cerr << error << std::endl; + + +#if defined(FML_OS_OHOS) +#define OHOS_LOG_TYPE_APP 0 +#define HILOG_LOG_DOMAIN 0 +#define HILOG_LOG_TAG "XcomFlutterEmbedder" + HiLog_LogLevel fx_severity = HILOG_LOG_ERROR; + (void ) OH_LOG_Print(0,fx_severity,HILOG_LOG_DOMAIN, HILOG_LOG_TAG,"%s",error ); +#endif return code; } diff --git a/shell/platform/embedder/fixtures/arc_end_caps.png b/shell/platform/embedder/fixtures/arc_end_caps.png index b9701b3a48c50a4a80baeeabc7ebf87492e13d48..149eea084895d2d90a42fa2d6e1af5313966a4cd 100644 Binary files a/shell/platform/embedder/fixtures/arc_end_caps.png and b/shell/platform/embedder/fixtures/arc_end_caps.png differ diff --git a/shell/platform/embedder/fixtures/dpr_noxform.png b/shell/platform/embedder/fixtures/dpr_noxform.png index ba557532583c3d6246f69949046b28bcf59137fe..9fca30cca1c37da1986fe7ee7ca3813df0e1cf4a 100644 Binary files a/shell/platform/embedder/fixtures/dpr_noxform.png and b/shell/platform/embedder/fixtures/dpr_noxform.png differ diff --git a/shell/platform/embedder/fixtures/dpr_xform.png b/shell/platform/embedder/fixtures/dpr_xform.png index 12e48fae67a4f1728b6f9c79faeb9c83bfa73c46..823f217aed68e7ab6831d93d2918716f1c91c908 100644 Binary files a/shell/platform/embedder/fixtures/dpr_xform.png and b/shell/platform/embedder/fixtures/dpr_xform.png differ diff --git a/shell/platform/embedder/fixtures/gradient.png b/shell/platform/embedder/fixtures/gradient.png index c4ae3b511f879a9bdc1c2950184faeb92a0ba0d4..f2f75f1136fa393b6e1ac481f68ec83709b1f4cd 100644 Binary files a/shell/platform/embedder/fixtures/gradient.png and b/shell/platform/embedder/fixtures/gradient.png differ diff --git a/shell/platform/embedder/fixtures/gradient_xform.png b/shell/platform/embedder/fixtures/gradient_xform.png index 575a82c27aafd607137d4d75a885bbe0134c69d2..f9ea3ad53968dc635b761b5dd7013731b22fe628 100644 Binary files a/shell/platform/embedder/fixtures/gradient_xform.png and b/shell/platform/embedder/fixtures/gradient_xform.png differ diff --git a/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png b/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png index 659473eedd28ab9cc2f67222e4b55496c97e3dca..38097bf97b876feffbb3010d5a2a0351e8886a9f 100644 Binary files a/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png and b/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png differ diff --git a/shell/platform/embedder/fixtures/vk_dpr_noxform.png b/shell/platform/embedder/fixtures/vk_dpr_noxform.png index 0689f1a414b1e33bc0b8c14c9252fac6e87a9e7a..7f5c28ba808fd59714b7cfe1113478d25ce02863 100644 Binary files a/shell/platform/embedder/fixtures/vk_dpr_noxform.png and b/shell/platform/embedder/fixtures/vk_dpr_noxform.png differ diff --git a/shell/platform/embedder/fixtures/vk_gradient.png b/shell/platform/embedder/fixtures/vk_gradient.png index 540bd8d66db9be38c0033f72750b23b49f536577..b00a8baf8d2e5301bd978fc12d212e15ab02ed6c 100644 Binary files a/shell/platform/embedder/fixtures/vk_gradient.png and b/shell/platform/embedder/fixtures/vk_gradient.png differ diff --git a/shell/platform/ohos/BUILD.gn b/shell/platform/ohos/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..305a7e2cf6ed6d9216500a209b18e0dcfd4250f8 --- /dev/null +++ b/shell/platform/ohos/BUILD.gn @@ -0,0 +1,566 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +# 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. + +assert(is_ohos) + +import("//build/config/ohos/config.gni") +import("//build/toolchain/clang.gni") +import("//flutter/build/bin_to_obj.gni") +import("//flutter/build/zip_bundle.gni") +import("//flutter/common/config.gni") +import("//flutter/shell/gpu/gpu.gni") +import("//flutter/shell/version/version.gni") +import("//flutter/testing/testing.gni") + +group("ohos") { + deps = [ + #":publish_headers_ohos", + #":ohos_har", + ":flutter_har_zip", + ":flutter_shell_native", + ":ohos_symbols", + ] + if (target_cpu != "x86") { + deps += [ ":gen_snapshot" ] + } +} +shell_gpu_configuration("ohos_gpu_configuration") { + enable_software = true + enable_gl = true + enable_vulkan = true + enable_metal = false +} + +# Temporary workaround for the issue describe in +# https://github.com/flutter/flutter/issues/14509 and +# https://github.com/flutter/flutter/issues/14438 +# Remove once the build infrastructure moves to Ubuntu 18.04 or newer, where +# the underlying issue is fixed. +config("disable_fatal_link_warnings") { + visibility = [ ":*" ] + ldflags = [ "-Wl,--no-fatal-warnings" ] +} + +_public_headers = [ + #"public/flutter_ohos/fl_view.h", + # "napi_common.h", + #"platform_view_ohos.h", +] + +config("relative_flutter_ohos_headers") { + include_dirs = [ "public" ] +} + +bin_to_assembly("icudtl_asm") { + deps = [] + input = "//flutter/third_party/icu/flutter/icudtl.dat" + symbol = "_binary_icudtl_dat_start" + size_symbol = "_binary_icudtl_dat_size" + executable = false +} + +source_set("flutter_ohos_sources") { + public = _public_headers + [ + "napi_common.h", + "ohos_xcomponent_adapter.h", + "./napi/platform_view_ohos_napi.h", + "ohos_shell_holder.h", + "platform_view_ohos.h", + "platform_message_response_ohos.h", + "platform_message_handler_ohos.h", + "vsync_waiter_ohos.h", + "ohos_logger.h", + "ohos_display.h", + "ohos_surface_software.h", + "./context/ohos_context.h", + "./surface/ohos_native_window.h", + "./surface/ohos_surface.h", + "ohos_touch_processor.h", + "ohos_context_gl_skia.h", + "ohos_context_vulkan_impeller.h", + "ohos_egl_surface.h", + "ohos_environment_gl.h", + "ohos_surface_gl_skia.h", + "ohos_surface_vulkan_impeller.h", + "ohos_external_texture.h", + "ohos_external_texture_gl.h", + "ohos_external_texture_vulkan.h", + "image_lru.h", + "types.h", + "ohos_logging.h", + "./utils/ohos_utils.h", + "./accessibility/ohos_semantics_node.h", + "./accessibility/ohos_semantics_tree.h", + "./accessibility/ohos_semantics_bridge.h", + ] + + #configs += [ "//flutter/shell/platform/ohos/config:gtk" ] + + sources = [ + "./context/ohos_context.cpp", + "./napi/platform_view_ohos_napi.cpp", + "./surface/ohos_native_window.cpp", + "./surface/ohos_snapshot_surface_producer.cpp", + "./surface/ohos_surface.cpp", + "library_loader.cpp", + "ohos_asset_provider.cpp", + "ohos_context_gl_impeller.cpp", + "ohos_context_vulkan_impeller.cpp", + + # "//flutter/impeller/toolkit/egl/display.cc", + "./accessibility/ohos_semantics_bridge.cpp", + "./accessibility/ohos_semantics_node.cpp", + "./accessibility/ohos_semantics_tree.cpp", + "./utils/ohos_utils.cpp", + "image_lru.cpp", + "ohos_context_gl_skia.cpp", + "ohos_display.cpp", + "ohos_egl_surface.cpp", + "ohos_environment_gl.cpp", + "ohos_external_texture.cpp", + "ohos_external_texture_gl.cpp", + "ohos_external_texture_vulkan.cpp", + "ohos_image_generator.cpp", + "ohos_logger.c", + "ohos_main.cpp", + "ohos_shell_holder.cpp", + "ohos_surface_gl_impeller.cpp", + "ohos_surface_gl_skia.cpp", + "ohos_surface_software.cpp", + "ohos_surface_vulkan_impeller.cpp", + "ohos_touch_processor.cpp", + "ohos_xcomponent_adapter.cpp", + "platform_message_handler_ohos.cpp", + "platform_message_response_ohos.cpp", + "platform_view_ohos.cpp", + "vsync_waiter_ohos.cpp", + ] + + sources += get_target_outputs(":icudtl_asm") + + # Set flag to stop headers being directly included (library users should not do this) + defines = [ + "FLUTTER_LINUX_COMPILATION", + "FLUTTER_ENGINE_NO_PROTOTYPES", + "OHOS_PLATFORM", + "__MUSL__", + ] + + deps = [ + "//flutter/shell/platform/common:common_cpp_input", + "//flutter/shell/platform/common:common_cpp_switches", + "//flutter/shell/platform/embedder:embedder_headers", + "//flutter/third_party/rapidjson", + ] + + public_deps = [ + ":icudtl_asm", + ":ohos_gpu_configuration", + "//flutter/assets", + "//flutter/common", + "//flutter/common/graphics", + "//flutter/flow", + "//flutter/fml", + "//flutter/impeller", + "//flutter/impeller/toolkit/egl", + "//flutter/lib/ui", + "//flutter/runtime", + "//flutter/runtime:libdart", + "//flutter/shell/common", + + #"//flutter/vulkan", + "//flutter/skia", + ] +} + +source_set("flutter_ohos_src") { + configs += [ + #"//flutter/shell/platform/ohos/config:gtk", + #"//flutter/shell/platform/ohos/config:epoxy", + ] + + defines = [ "FLUTTER_ENGINE_NO_PROTOTYPES" ] + + public_deps = [ ":flutter_ohos_sources" ] +} + +test_fixtures("flutter_ohos_fixtures") { + fixtures = [] +} + +executable("flutter_ohos_unittests") { + testonly = true + + sources = [ + #"testing/mock_texture_registrar.cc", + "ohos_asset_provider_unittests.cpp", + ] + + public_configs = [ "//flutter:config" ] + + defines = [ + "FLUTTER_ENGINE_NO_PROTOTYPES", + + # Set flag to allow public headers to be directly included + # (library users should not do this) + "FLUTTER_LINUX_COMPILATION", + ] + + deps = [ + ":flutter_ohos_fixtures", + ":flutter_ohos_sources", + "//flutter/runtime:libdart", + "//flutter/shell/platform/embedder:embedder_headers", + "//flutter/shell/platform/embedder:embedder_test_utils", + "//flutter/testing", + "//flutter/third_party/googletest:gmock", + "//flutter/third_party/googletest:gtest", + ] + + ldflags = [ + "--rtlib=compiler-rt", + "-fuse-ld=lld", + "-static-libstdc++", + ] + + ldflags += [ "-lnative_window" ] + ldflags += [ "-lnative_vsync" ] + ldflags += [ "-lace_napi.z" ] + ldflags += [ "-lace_ndk.z" ] + ldflags += [ "-lhilog_ndk.z" ] + ldflags += [ "-luv" ] + ldflags += [ "-lrawfile.z" ] + ldflags += [ "-lEGL" ] + ldflags += [ "-lGLESv3" ] + + ldflags += [ "-limage_ndk.z" ] + ldflags += [ "-lnative_image" ] + + ldflags += [ "-lm" ] + ldflags += [ "-lpthread" ] + ldflags += [ "-lpixelmap_ndk.z" ] + ldflags += [ "-lpixelmap" ] + ldflags += [ "-limage_source" ] + ldflags += [ "-lqos" ] + ldflags += [ "-ldeviceinfo_ndk.z" ] +} + +shared_library("flutter_shell_native") { + output_name = "flutter" + deps = [ ":flutter_ohos_src" ] + + ldflags = [ + "--rtlib=compiler-rt", + "-fuse-ld=lld", + + #"-static-libstdc++", + + # "-Wl", "--build-id=sha1", + # "-Wl,","--warn-shared-textrel", + # "-Wl,","--fatal-warnings -lunwind", + # "-Wl,","--no-undefined -Qunused-arguments", + # "-Wl,","-z,noexecstack" + ] + + #ldflags = [ "-Wl,-rpath,\$ORIGIN" ] + #ldflags += ["-L{$OHOS_NDK_LIB}","-lnative_window"] + ldflags += [ "-lnative_window" ] + ldflags += [ "-lnative_vsync" ] + ldflags += [ "-lace_napi.z" ] + ldflags += [ "-lace_ndk.z" ] + ldflags += [ "-lhilog_ndk.z" ] + ldflags += [ "-luv" ] + ldflags += [ "-lrawfile.z" ] + ldflags += [ "-lEGL" ] + ldflags += [ "-lGLESv3" ] + + # ldflags += ["-lGLESv2"] + ldflags += [ "-limage_ndk.z" ] + ldflags += [ "-lnative_image" ] + + ldflags += [ "-lm" ] + ldflags += [ "-lpthread" ] + ldflags += [ "-lpixelmap_ndk.z" ] + ldflags += [ "-lpixelmap" ] + ldflags += [ "-limage_source" ] + ldflags += [ "-lqos" ] + + public_configs = [ "//flutter:config" ] +} + +#copy("publish_headers_ohos") { +# sources = _public_headers +# outputs = [ "$root_out_dir/flutter_ohos/{{source_file_part}}" ] +#} + +#zip_bundle("flutter_gtk") { +# prefix = "$full_target_platform_name/" +# if (flutter_runtime_mode != "debug" || +# (flutter_runtime_mode == "debug" && target_cpu != "x64")) { +# prefix = "$full_target_platform_name-$flutter_runtime_mode/" +# } +# output = "${prefix}${full_target_platform_name}-flutter-gtk.zip" +# deps = [ +# ":flutter_ohos_gtk", +# ":publish_headers_ohos", +# "//third_party/dart/runtime/bin:gen_snapshot", +# ] +# sources = get_target_outputs(":publish_headers_ohos") +# tmp_files = [] +# foreach(source, sources) { +# tmp_files += [ +# { +# source = source +# destination = rebase_path(source, "$root_build_dir") +# }, +# ] +# } +# tmp_files += [ +# { +# source = "$root_build_dir/libflutter_${host_os}_gtk.so" +# destination = "libflutter_${host_os}_gtk.so" +# }, +# { +# source = "$root_build_dir/gen_snapshot" +# destination = "gen_snapshot" +# }, +# ] +# files = tmp_files +#} + +declare_args() { + embedding_artifact_id = "flutter_embedding_$flutter_runtime_mode" + embedding_har_filename = "$embedding_artifact_id.har" + embedding_har_path = "$root_out_dir/$embedding_har_filename" + engine_artifact_id = + string_replace(ohos_app_abi, "-", "_") + "_" + flutter_runtime_mode + engine_har_filename = "$engine_artifact_id.har" + + ohos_zip_archive_dir = "ohos-$target_cpu" + if (flutter_runtime_mode != "debug") { + ohos_zip_archive_dir += "-$flutter_runtime_mode" + } + ohos_api_int = 12 +} + +action("ohos_har") { + script = "//flutter/attachment/scripts/ohos_create_flutter_har.py" + + if (stripped_symbols) { + engine_library = "libflutter.so" + } else { + engine_library = "so.unstripped/libflutter.so" + } + + inputs = [ + # "$root_build_dir/$embedding_har_filename", + "$root_build_dir/$engine_library", + ] + + outputs = [ + "$root_build_dir/flutter.har", + # "$root_build_dir/$engine_har_filename", + ] + + args = [ + "--embedding_src", + "../../flutter/shell/platform/ohos/flutter_embedding", + "--build_dir", + "./obj/ohos/flutter_embedding", + "--build_type", + flutter_runtime_mode, + "--output", + rebase_path("flutter.har", root_build_dir, root_build_dir), + "--native_lib", + rebase_path("$engine_library", root_build_dir, root_build_dir), + "--ohos_abi", + ohos_app_abi, + "--ohos_api_int", + "$ohos_api_int", + ] + + deps = [ ":flutter_shell_native" ] + + if (flutter_runtime_mode == "profile") { + deps += [ "//flutter/shell/vmservice:vmservice_snapshot" ] + args += [ + "--native_lib", + rebase_path( + "$root_gen_dir/flutter/shell/vmservice/ohos/libs/$ohos_app_abi/libvmservice_snapshot.so", + root_build_dir, + root_build_dir), + ] + } +} + +zip_bundle("flutter_har_zip") { + output = "$ohos_zip_archive_dir/artifacts.zip" + files = [ + { + source = "$root_build_dir/flutter.har" + destination = "flutter.har" + }, + ] + + deps = [ ":ohos_har" ] +} + +zip_bundle("ohos_symbols") { + output = "$ohos_zip_archive_dir/symbols.zip" + files = [ + { + source = "$root_build_dir/so.unstripped/libflutter.so" + destination = "libflutter.so" + }, + ] + + deps = [ ":flutter_shell_native" ] +} + +generated_file("ohos_entitlement_config") { + outputs = [ "$target_gen_dir/ohos_entitlements.txt" ] + contents = [ "gen_snapshot" ] + deps = [] +} + +if (target_cpu != "x86") { + zip_bundle("gen_snapshot") { + gen_snapshot_bin = "gen_snapshot" + gen_snapshot_out_dir = get_label_info( + "//third_party/dart/runtime/bin:gen_snapshot($host_toolchain)", + "root_out_dir") + gen_snapshot_path = rebase_path("$gen_snapshot_out_dir/$gen_snapshot_bin") + + if (host_os == "linux") { + output = "$ohos_zip_archive_dir/linux-x64.zip" + } else if (host_os == "mac") { + output = "$ohos_zip_archive_dir/darwin-x64.zip" + } else if (host_os == "win") { + output = "$ohos_zip_archive_dir/windows-x64.zip" + gen_snapshot_bin = "gen_snapshot.exe" + gen_snapshot_path = rebase_path("$root_out_dir/$gen_snapshot_bin") + } + + files = [ + { + source = gen_snapshot_path + destination = gen_snapshot_bin + }, + ] + + deps = [ "//third_party/dart/runtime/bin:gen_snapshot($host_toolchain)" ] + } + + # TODO(godofredoc): Remove gen_snapshot and rename new_gen_snapshot when v2 migration is complete. + # BUG: https://github.com/flutter/flutter/issues/105351 + zip_bundle("new_gen_snapshot") { + gen_snapshot_bin = "gen_snapshot" + gen_snapshot_out_dir = get_label_info( + "//third_party/dart/runtime/bin:gen_snapshot($host_toolchain)", + "root_out_dir") + gen_snapshot_path = rebase_path("$gen_snapshot_out_dir/$gen_snapshot_bin") + + if (host_os == "linux") { + output = "$ohos_zip_archive_dir/$full_platform_name-$flutter_runtime_mode/linux-x64.zip" + } else if (host_os == "mac") { + output = "$ohos_zip_archive_dir/$full_platform_name-$flutter_runtime_mode/darwin-x64.zip" + } else if (host_os == "win") { + output = "$ohos_zip_archive_dir/$full_platform_name-$flutter_runtime_mode/windows-x64.zip" + gen_snapshot_bin = "gen_snapshot.exe" + gen_snapshot_path = rebase_path("$root_out_dir/$gen_snapshot_bin") + } + + files = [ + { + source = gen_snapshot_path + destination = gen_snapshot_bin + }, + ] + + deps = [ "//third_party/dart/runtime/bin:gen_snapshot($host_toolchain)" ] + + if (host_os == "mac") { + deps += [ ":ohos_entitlement_config" ] + files += [ + { + source = "$target_gen_dir/ohos_entitlements.txt" + destination = "entitlements.txt" + }, + ] + } + } +} + +if (target_cpu == "x64" || target_cpu == "arm64") { + zip_bundle("analyze_snapshot") { + deps = + [ "//third_party/dart/runtime/bin:analyze_snapshot($host_toolchain)" ] + + analyze_snapshot_bin = "analyze_snapshot" + analyze_snapshot_out_dir = + get_label_info( + "//third_party/dart/runtime/bin:analyze_snapshot($host_toolchain)", + "root_out_dir") + analyze_snapshot_path = + rebase_path("$analyze_snapshot_out_dir/$analyze_snapshot_bin") + + if (host_os == "linux") { + output = "$ohos_zip_archive_dir/analyze-snapshot-linux-x64.zip" + } else if (host_os == "mac") { + output = "$ohos_zip_archive_dir/analyze-snapshot-darwin-x64.zip" + } else if (host_os == "win") { + output = "$ohos_zip_archive_dir/analyze-snapshot-windows-x64.zip" + analyze_snapshot_bin = "analyze-snapshot.exe" + analyze_snapshot_path = rebase_path("$root_out_dir/$analyze_snapshot_bin") + } + + files = [ + { + source = analyze_snapshot_path + destination = analyze_snapshot_bin + }, + ] + } + + # TODO(godofredoc): Remove analyze_snapshot and rename new_analyze_snapshot when v2 migration is complete. + # BUG: https://github.com/flutter/flutter/issues/105351 + zip_bundle("new_analyze_snapshot") { + deps = + [ "//third_party/dart/runtime/bin:analyze_snapshot($host_toolchain)" ] + + analyze_snapshot_bin = "analyze_snapshot" + analyze_snapshot_out_dir = + get_label_info( + "//third_party/dart/runtime/bin:analyze_snapshot($host_toolchain)", + "root_out_dir") + analyze_snapshot_path = + rebase_path("$analyze_snapshot_out_dir/$analyze_snapshot_bin") + + if (host_os == "linux") { + output = "$ohos_zip_archive_dir/$full_platform_name-$flutter_runtime_mode/analyze-snapshot-linux-x64.zip" + } else if (host_os == "mac") { + output = "$ohos_zip_archive_dir/$full_platform_name-$flutter_runtime_mode/analyze-snapshot-darwin-x64.zip" + } else if (host_os == "win") { + output = "$ohos_zip_archive_dir/$full_platform_name-$flutter_runtime_mode/analyze-snapshot-windows-x64.zip" + analyze_snapshot_bin = "analyze-snapshot.exe" + analyze_snapshot_path = rebase_path("$root_out_dir/$analyze_snapshot_bin") + } + + files = [ + { + source = analyze_snapshot_path + destination = analyze_snapshot_bin + }, + ] + } +} diff --git a/shell/platform/ohos/accessibility/ohos_semantics_bridge.cpp b/shell/platform/ohos/accessibility/ohos_semantics_bridge.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e300c24cc46bc8da8f6197547b17ef1ec64fbbef --- /dev/null +++ b/shell/platform/ohos/accessibility/ohos_semantics_bridge.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * 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 "ohos_semantics_bridge.h" +#include +#include +#include + +namespace flutter { + +void SemanticsBridge::UpdateNodeTree(flutter::SemanticsNodeUpdates& nodes) { + auto update_nodes = tree_.UpdateWithNodes(nodes); + + for (auto node : update_nodes) { + // if node perform scroll + if (node->scrollChanged && node->performScrollAction) { + std::string child_str = ""; + for (auto id : node->childrenInTraversalOrder) { + child_str += std::to_string(id) + ","; + } + FML_DLOG(INFO) << node->id << " scroll index " << node->scrollIndex + << " current " << node->scrollCurrentIndex << " end " + << node->scrollEndIndex << " child num " + << node->scrollChildren << " children:" << child_str + << " has update " << node->hasUpdate << " " + << node->scrollVisibleEndIndex; + child_str = ""; + for (auto node : node->childrenInTraversalOrderList) { + if (node->isExist && node->IsVisible()) { + child_str += std::to_string(node->id) + ","; + } + } + FML_DLOG(INFO) << node->id << " scroll index " << node->scrollIndex + << " end " << node->scrollEndIndex << " given child num " + << node->scrollChildren + << " visible children:" << child_str; + + if (node->scrollEndIndex != -1 && node->scrollCurrentIndex != -1) { + SendSemanticsEvent(node, ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_SCROLLED, + nullptr); + FML_DLOG(INFO) << node->id << " update scroll "; + node->scrollChanged = false; + node->performScrollAction = false; + } + } + + // text is selected + if (node->performSelectAction && node->selectChanged) { + SendSemanticsEvent(node, ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_SELECTED, + nullptr); + node->selectChanged = false; + node->performSelectAction = false; + } + } + + UpdateFocusedNode(); +} + +void SemanticsBridge::UpdateFocusedNode() { + auto focused_node = tree_.focused_node_; + + auto root_node = tree_.GetRootNode(); + if (has_navigationed_ && root_node != nullptr) { + has_navigationed_ = false; + SendSemanticsEvent( + root_node, ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_CONTENT_UPDATE, + nullptr); + } + + if (focused_node && focused_node->hasUpdate) { + SendSemanticsEvent(focused_node, + ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_FOCUS_NODE_UPDATE, + nullptr); + } + + // If focused_node disppear, we need request next focusable_node. + if (tree_.need_request_focused_node_ && !tree_.focus_request_has_send_) { + FML_DLOG(INFO) << "UpdateFocusedNode request_next_node " + << tree_.need_request_focused_node_->id << " node content " + << tree_.need_request_focused_node_->contentString; + SendSemanticsEvent( + tree_.need_request_focused_node_, + ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_REQUEST_ACCESSIBILITY_FOCUS, + nullptr); + tree_.focus_request_has_send_ = true; + } + + return; +} + +SemanticsNodeExtend* SemanticsBridge::GetNodeById(int32_t id) { + return tree_.FindNodeById(id); +} + +void SemanticsBridge::SendSemanticsEvent(SemanticsNodeExtend* node, + ArkUI_AccessibilityEventType type, + const char* message) { + if (!is_accessibility_enabled_) { + return; + } + auto event = OH_ArkUI_CreateAccessibilityEventInfo(); + if (node) { + OH_ArkUI_AccessibilityEventSetElementInfo(event, node->elementInfoOHOS); + node->hasUpdate = false; + } + OH_ArkUI_AccessibilityEventSetEventType(event, type); + if (type == + ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_ANNOUNCE_FOR_ACCESSIBILITY && + message != nullptr) { + OH_ArkUI_AccessibilityEventSetTextAnnouncedForAccessibility(event, message); + } + if (provider_ohos_) { + auto callback = [](int32_t errorCode) { + if (errorCode != 0) { + FML_DLOG(INFO) << "SendSemanticsEvent callback-> errorCode =" + << errorCode; + } + }; + OH_ArkUI_SendAccessibilityAsyncEvent(provider_ohos_, event, callback); + FML_DLOG(INFO) << "SendSemanticsEvent type " << (int64_t)type + << " node:" << (node ? node->id : -1) << " visible " + << node->IsVisible() + << " message:" << (message ? message : ""); + } + + OH_ArkUI_DestoryAccessibilityEventInfo(event); +} + +int32_t SemanticsBridge::FindFocusNode(int32_t id, + ArkUI_AccessibilityFocusType focusType, + ArkUI_AccessibilityElementInfo* info) { + auto node = tree_.FindFocusNode(id, focusType); + if (node) { + node->FillElementInfo(info); + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_SUCCESSFUL; + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +int32_t SemanticsBridge::FindNextFocusNode( + int32_t id, + ArkUI_AccessibilityFocusMoveDirection direction, + ArkUI_AccessibilityElementInfo* info) { + auto node = tree_.FindNextFocusNode(id, direction); + if (node) { + node->FillElementInfo(info); + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_SUCCESSFUL; + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +int32_t SemanticsBridge::FillNodesWithSearchText( + int32_t id, + const char* text, + ArkUI_AccessibilityElementInfoList* list) { + if (tree_.FillNodesWithSearchText(id, text, list)) { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_SUCCESSFUL; + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +int32_t SemanticsBridge::FillNodesWithSearch( + int32_t id, + ArkUI_AccessibilitySearchMode mode, + ArkUI_AccessibilityElementInfoList* list) { + if (tree_.FillNodesWithSearch(id, mode, list)) { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_SUCCESSFUL; + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +int32_t SemanticsBridge::ClearAccessibilityFocus(int32_t id) { + auto node = tree_.FindFocusNode( + -1, ARKUI_ACCESSIBILITY_NATIVE_FOCUS_TYPE_ACCESSIBILITY); + FML_DLOG(INFO) << "ClearAccessibilityFocus " << id; + if (node && (node->id == id || id == 0)) { + tree_.ClearAccessibilityFocusNode(); + SendSemanticsEvent( + node, ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_ACCESSIBILITY_FOCUS_CLEARED, + nullptr); + } + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_SUCCESSFUL; +} + +int32_t SemanticsBridge::GainAccessibilityFocus(int32_t id) { + if (tree_.SetAccessibilityFocusNode(id)) { + auto node = tree_.FindNodeById(id); + FML_DLOG(INFO) << "GainAccessibilityFocus " << id; + SendSemanticsEvent( + node, ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_ACCESSIBILITY_FOCUSED, + nullptr); + // if node has update + if (node->hasUpdate) { + SendSemanticsEvent( + node, ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_FOCUS_NODE_UPDATE, + nullptr); + } + if (node == tree_.need_request_focused_node_) { + tree_.need_request_focused_node_ = nullptr; + tree_.focus_request_has_send_ = false; + } + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_SUCCESSFUL; + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +int32_t SemanticsBridge::GetAccessibilityNodeCursorPosition(int64_t elementId, + int32_t* index) { + auto node = tree_.FindNodeById(elementId); + if (node) { + *index = node->textSelectionBase; + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_SUCCESSFUL; + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +void SemanticsBridge::Announce(std::unique_ptr& message) { + SendSemanticsEvent( + nullptr, ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_ANNOUNCE_FOR_ACCESSIBILITY, + message.get()); + FML_DLOG(INFO) << "Announce -> message: " << message.get(); +} + +void SemanticsBridge::OnTap(int32_t nodeId) { + SendSemanticsEvent(tree_.FindNodeById(nodeId), + ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_CLICKED, nullptr); + FML_DLOG(INFO) << "OnTap -> nodeId: " << nodeId; +} + +void SemanticsBridge::OnLongPress(int32_t nodeId) { + SendSemanticsEvent(tree_.FindNodeById(nodeId), + ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_LONG_CLICKED, + nullptr); + FML_DLOG(INFO) << "OnLongPress -> nodeId: " << nodeId; +} + +void SemanticsBridge::OnTooltip(std::unique_ptr& message) { + SendSemanticsEvent(tree_.FindNodeById(0), + ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_STATE_UPDATE, + nullptr); + SendSemanticsEvent( + nullptr, ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_ANNOUNCE_FOR_ACCESSIBILITY, + message.get()); + FML_DLOG(INFO) << "OnTooltip -> message: " << message.get(); +} + +void SemanticsBridge::OnAccessibilityStateChange(bool state) { + is_accessibility_enabled_ = state; +} + +void SemanticsBridge::OnAccessibilityNavigation(bool is_nav) { + has_navigationed_ = is_nav; +} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/accessibility/ohos_semantics_bridge.h b/shell/platform/ohos/accessibility/ohos_semantics_bridge.h new file mode 100644 index 0000000000000000000000000000000000000000..6057e30e1820f12df02e524e9b99d78d999b00a1 --- /dev/null +++ b/shell/platform/ohos/accessibility/ohos_semantics_bridge.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_ACCESSIBILITY_OHOS_SEMANTICS_BRIDGE_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_ACCESSIBILITY_OHOS_SEMANTICS_BRIDGE_H_ + +#include "ohos_semantics_tree.h" + +namespace flutter { +class SemanticsBridge { + public: + SemanticsBridge() = default; + + SemanticsTree tree_; + ArkUI_AccessibilityProvider* provider_ohos_ = nullptr; + ArkUI_AccessibilityProvider* old_provider_ohos_ = nullptr; + bool is_accessibility_enabled_ = false; + bool has_navigationed_ = false; + void UpdateNodeTree(flutter::SemanticsNodeUpdates& nodes); + void UpdateFocusedNode(); + SemanticsNodeExtend* GetNodeById(int32_t id); + + void SendSemanticsEvent(SemanticsNodeExtend* node, + ArkUI_AccessibilityEventType type, + const char* message); + + int32_t FindFocusNode(int32_t id, + ArkUI_AccessibilityFocusType focusType, + ArkUI_AccessibilityElementInfo* info); + + int32_t FindNextFocusNode(int32_t id, + ArkUI_AccessibilityFocusMoveDirection direction, + ArkUI_AccessibilityElementInfo* info); + + int32_t FillNodesWithSearchText(int32_t id, + const char* text, + ArkUI_AccessibilityElementInfoList* list); + + int32_t FillNodesWithSearch(int32_t id, + ArkUI_AccessibilitySearchMode mode, + ArkUI_AccessibilityElementInfoList* list); + + int32_t ClearAccessibilityFocus(int32_t id); + int32_t GainAccessibilityFocus(int32_t id); + + int32_t GetAccessibilityNodeCursorPosition(int64_t elementId, int32_t* index); + + void Announce(std::unique_ptr& message); + void OnTap(int32_t nodeId); + void OnLongPress(int32_t nodeId); + void OnTooltip(std::unique_ptr& message); + void OnAccessibilityStateChange(bool state); + void OnAccessibilityNavigation(bool is_nav); +}; +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_ACCESSIBILITY_OHOS_SEMANTICS_BRIDGE_H_ \ No newline at end of file diff --git a/shell/platform/ohos/accessibility/ohos_semantics_node.cpp b/shell/platform/ohos/accessibility/ohos_semantics_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bd6e368285f4820544f07813d8ae0f8a078c3866 --- /dev/null +++ b/shell/platform/ohos/accessibility/ohos_semantics_node.cpp @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * 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 "ohos_semantics_node.h" +#include +#include +#include +#include +#include "flutter/shell/platform/ohos/ohos_logging.h" +#include "ohos_semantics_tree.h" + +namespace flutter { +void SemanticsNodeExtend::FillElementInfo( + ArkUI_AccessibilityElementInfo* info) { + if (!info) { + return; + } + + FillElementInfoWithId(info); + FillElementInfoWithProperty(info); + FillElementInfoWithContent(info); + FillElementInfoWithChildren(info); + FillElementInfoWithParent(info); + FillElementInfoWithScroll(info); + FillElementInfoWithRect(info); + FillElementInfoWithSelect(info); +} + +void SemanticsNodeExtend::UpdateSelfElementInfo() { + if (!hasInit || idChanged) { + FillElementInfoWithId(elementInfoOHOS); + idChanged = false; + hasUpdate = true; + } + if (!hasInit || propertyChanged) { + FillElementInfoWithProperty(elementInfoOHOS); + propertyChanged = false; + hasUpdate = true; + } + if (!hasInit || contentChanged) { + FillElementInfoWithContent(elementInfoOHOS); + contentChanged = false; + hasUpdate = true; + } + if (!hasInit || childrenChanged) { + FillElementInfoWithChildren(elementInfoOHOS); + childrenChanged = false; + hasUpdate = true; + } + if (!hasInit || parentChanged) { + FillElementInfoWithParent(elementInfoOHOS); + parentChanged = false; + hasUpdate = true; + } + if (!hasInit || scrollChanged) { + FillElementInfoWithScroll(elementInfoOHOS); + hasUpdate = true; + // scroll event need sent, so we don't make it false. + // scrollChanged = false; + } + if (!hasInit || rectChanged) { + FillElementInfoWithRect(elementInfoOHOS); + rectChanged = false; + hasUpdate = true; + } + if (!hasInit || selectChanged) { + FillElementInfoWithSelect(elementInfoOHOS); + hasUpdate = true; + // select event need sent, so we don't make it false. + // selectChanged = false; + } + hasInit = true; +} + +void SemanticsNodeExtend::FillElementInfoWithId( + ArkUI_AccessibilityElementInfo* info) { + OH_ArkUI_AccessibilityElementInfoSetElementId(info, id); + OH_ArkUI_AccessibilityElementInfoSetAccessibilityGroup(info, false); +} + +void SemanticsNodeExtend::FillElementInfoWithProperty( + ArkUI_AccessibilityElementInfo* info) { + OH_ArkUI_AccessibilityElementInfoSetScrollable(info, IsScrollable()); + OH_ArkUI_AccessibilityElementInfoSetLongClickable(info, IsHasLongPress()); + OH_ArkUI_AccessibilityElementInfoSetClickable(info, IsClickable()); + + OH_ArkUI_AccessibilityElementInfoSetEnabled(info, IsEnabled()); + OH_ArkUI_AccessibilityElementInfoSetFocused(info, IsFocused()); + OH_ArkUI_AccessibilityElementInfoSetIsPassword(info, IsPassword()); + OH_ArkUI_AccessibilityElementInfoSetCheckable(info, IsCheckable()); + OH_ArkUI_AccessibilityElementInfoSetChecked(info, IsChecked()); + OH_ArkUI_AccessibilityElementInfoSetVisible(info, IsVisible()); + OH_ArkUI_AccessibilityElementInfoSetSelected(info, IsSelected()); + OH_ArkUI_AccessibilityElementInfoSetEditable(info, IsEditable()); + OH_ArkUI_AccessibilityElementInfoSetFocusable(info, IsFocusable()); + + OH_ArkUI_AccessibilityElementInfoSetComponentType(info, componentType); + OH_ArkUI_AccessibilityElementInfoSetOperationActions(info, ohActions.size(), + ohActions.data()); +} + +void SemanticsNodeExtend::FillElementInfoWithContent( + ArkUI_AccessibilityElementInfo* info) { + OH_ArkUI_AccessibilityElementInfoSetAccessibilityText(info, value.c_str()); + if (IsTextField()) { + OH_ArkUI_AccessibilityElementInfoSetHintText(info, GetHintText().c_str()); + } else { + contentString = GetContents(); + OH_ArkUI_AccessibilityElementInfoSetContents(info, contentString.c_str()); + } +} + +void SemanticsNodeExtend::FillElementInfoWithChildren( + ArkUI_AccessibilityElementInfo* info) { + // childrenInTraversalOrderList may less then childrenInTraversalOrder + OH_ArkUI_AccessibilityElementInfoSetChildNodeIds( + info, existChildrenInTraversalOrder.size(), + existChildrenInTraversalOrder.data()); +} + +void SemanticsNodeExtend::FillElementInfoWithParent( + ArkUI_AccessibilityElementInfo* info) { + if (id == 0) { + OH_ArkUI_AccessibilityElementInfoSetParentId( + info, kArkuiAccessibilityRootParentId); + } else { + OH_ArkUI_AccessibilityElementInfoSetParentId(info, parentId); + } +} + +void SemanticsNodeExtend::FillElementInfoWithScroll( + ArkUI_AccessibilityElementInfo* info) { + if (scrollChildren > 0) { + OH_ArkUI_AccessibilityElementInfoSetItemCount(info, scrollChildren); + OH_ArkUI_AccessibilityElementInfoSetStartItemIndex(info, scrollIndex); + OH_ArkUI_AccessibilityElementInfoSetEndItemIndex(info, scrollEndIndex); + OH_ArkUI_AccessibilityElementInfoSetAccessibilityOffset(info, + scrollPosition); + // todo check current index + // if (scrollCurrentIndex != -1) { + // OH_ArkUI_AccessibilityElementInfoSetCurrentItemIndex(info, + // scrollIndex); + // } + } +} + +void SemanticsNodeExtend::FillElementInfoWithRect( + ArkUI_AccessibilityElementInfo* info) { + ArkUI_AccessibleRect rect = { + static_cast(absoluteRect.fLeft), + static_cast(absoluteRect.fTop), + static_cast(absoluteRect.fRight), + static_cast(absoluteRect.fBottom), + }; + OH_ArkUI_AccessibilityElementInfoSetScreenRect(info, &rect); +} + +void SemanticsNodeExtend::FillElementInfoWithSelect( + ArkUI_AccessibilityElementInfo* info) { + if (textSelectionBase != -1 && textSelectionExtent != -1) { + OH_ArkUI_AccessibilityElementInfoSetSelectedTextStart(info, + textSelectionBase); + OH_ArkUI_AccessibilityElementInfoSetSelectedTextEnd(info, + textSelectionExtent); + } +} + +void SemanticsNodeExtend::OHOSComponentTypeUpdate() { + if (id == 0) { + componentType = OHWidgetName::kRootWidgetName; + } else if (HasFlag(FLAGS_::kIsButton)) { + componentType = OHWidgetName::kButtonWidgetName; + } else if (HasFlag(FLAGS_::kIsTextField)) { + componentType = OHWidgetName::kEditTextWidgetName; + } else if (HasFlag(FLAGS_::kIsMultiline)) { + componentType = OHWidgetName::kEditMultilineTextWidgetName; + } else if (HasFlag(FLAGS_::kIsLink)) { + componentType = OHWidgetName::kLinkWidgetName; + } else if (HasFlag(FLAGS_::kIsSlider) || HasAction(ACTIONS_::kIncrease) || + HasAction(ACTIONS_::kDecrease)) { + componentType = OHWidgetName::kSliderWidgetName; + } else if (HasFlag(FLAGS_::kIsHeader)) { + componentType = OHWidgetName::kHeaderWidgetName; + } else if (HasFlag(FLAGS_::kIsImage)) { + componentType = OHWidgetName::kImageWidgetName; + } else if (HasFlag(FLAGS_::kHasCheckedState)) { + if (HasFlag(FLAGS_::kIsInMutuallyExclusiveGroup)) { + // arkui没有RadioButton,这里透传为RadioButton + componentType = OHWidgetName::kRadioButtonWidgetName; + } else { + componentType = OHWidgetName::kCheckBoxWidgetName; + } + } else if (HasFlag(FLAGS_::kHasToggledState)) { + componentType = OHWidgetName::kSwitchWidgetName; + } else if (HasAction(ACTIONS_::kIncrease) || HasAction(ACTIONS_::kDecrease)) { + componentType = OHWidgetName::kSeekbarWidgetName; + } else if (HasFlag(FLAGS_::kHasImplicitScrolling)) { + componentType = OHWidgetName::kScrollWidgetName; + } else if ((!label.empty() || !tooltip.empty() || !hint.empty())) { + componentType = OHWidgetName::kTextWidgetName; + } else { + componentType = OHWidgetName::kOtherWidgetName; + } +} + +void SemanticsNodeExtend::OHOSActionsUpdate() { + ohActions.clear(); + int32_t actionTypeNum = 0; + if (HasAction(ACTIONS_::kTap)) { + ohActions.push_back({ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLICK, + "点击操作"}); + } + if (HasAction(ACTIONS_::kLongPress)) { + ohActions.push_back({ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_LONG_CLICK, + "长按操作"}); + } + if (HasFlag(SemanticsFlags::kHasImplicitScrolling) && + HasAction(ACTIONS_::kScrollLeft)) { + ohActions.push_back( + {ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_FORWARD, + "向左滑动"}); + } + if (HasFlag(SemanticsFlags::kHasImplicitScrolling) && + HasAction(ACTIONS_::kScrollRight)) { + ohActions.push_back( + {ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_BACKWARD, + "向右滑动"}); + } + if (HasFlag(SemanticsFlags::kHasImplicitScrolling) && + HasAction(ACTIONS_::kScrollUp)) { + ohActions.push_back( + {ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_FORWARD, + "向上滑动"}); + } + if (HasFlag(SemanticsFlags::kHasImplicitScrolling) && + HasAction(ACTIONS_::kScrollDown)) { + ohActions.push_back( + {ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_BACKWARD, + "向下滑动"}); + } + if (HasAction(ACTIONS_::kIncrease)) { + ohActions.push_back( + {ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_FORWARD, + "增加"}); + } + if (HasAction(ACTIONS_::kDecrease)) { + ohActions.push_back( + {ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_BACKWARD, + "减少"}); + } + if (HasAction(ACTIONS_::kShowOnScreen)) { + } + if (HasAction(ACTIONS_::kSetSelection)) { + ohActions.push_back({ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SELECT_TEXT, + "文本选择"}); + } + if (HasAction(ACTIONS_::kCopy)) { + ohActions.push_back({ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_COPY, + "文本复制"}); + } + if (HasAction(ACTIONS_::kCut)) { + ohActions.push_back({ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CUT, + "文本剪切"}); + } + if (HasAction(ACTIONS_::kPaste)) { + ohActions.push_back({ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_PASTE, + "文本粘贴"}); + } + // We need the isVisible check to allow the accessibility framework to perform + // a scroll action when the Scroll component focuses on the next invisible + // node. + if (HasAction(ACTIONS_::kDidGainAccessibilityFocus) || + (IsFocusable() && IsVisible())) { + ohActions.push_back( + {ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_GAIN_ACCESSIBILITY_FOCUS, + "获取焦点"}); + } + if (HasAction(ACTIONS_::kDidLoseAccessibilityFocus) || + (IsFocusable() && IsVisible())) { + ohActions.push_back( + {ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLEAR_ACCESSIBILITY_FOCUS, + "清除焦点"}); + } + if (HasAction(ACTIONS_::kCustomAction)) { + } + if (HasAction(ACTIONS_::kDismiss)) { + } + if (HasAction(ACTIONS_::kMoveCursorForwardByWord) || + HasAction(ACTIONS_::kMoveCursorBackwardByWord) || + HasAction(ACTIONS_::kMoveCursorForwardByCharacter) || + HasAction(ACTIONS_::kMoveCursorBackwardByCharacter)) { + // @todo we don't support this. + ohActions.push_back( + {ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SET_CURSOR_POSITION, + "光标位置设置"}); + } + if (HasAction(ACTIONS_::kSetText)) { + ohActions.push_back({ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SET_TEXT, + "文本内容设置"}); + } +} + +void SemanticsNodeExtend::UpdateSelfRecursively( + std::unordered_set& visitorId, + SkM44& fatherTransform, + bool needUpdate) { + assert(visitorId.find(id) == visitorId.end()); + visitorId.insert(id); + if (rectChanged) { + needUpdate = true; + } + if (needUpdate) { + auto [left, top, right, bottom] = rect; + absoluteTransform = SkM44(fatherTransform, transform); + SkV4 points[4] = { + {left, top, 0, 1}, + {right, top, 0, 1}, + {right, bottom, 0, 1}, + {left, bottom, 0, 1}, + }; + points[0] = absoluteTransform * points[0]; + points[1] = absoluteTransform * points[1]; + points[2] = absoluteTransform * points[2]; + points[3] = absoluteTransform * points[3]; + setAbsoluteRect( + std::min({points[0].x, points[1].x, points[2].x, points[3].x}), + std::min({points[0].y, points[1].y, points[2].y, points[3].y}), + std::max({points[0].x, points[1].x, points[2].x, points[3].x}), + std::max({points[0].y, points[1].y, points[2].y, points[3].y})); + // Update rect info: it need the father node's rect. + rectChanged = true; + } + + SemanticsNodeExtend* prevNode = nullptr; + int visible_num = 0; + focusableInSubtree = IsFocusable(); + int last_visible_index = 0; + int child_index = 0; + + std::vector exist_children; + for (auto& childNode : childrenInTraversalOrderList) { + if (!childNode->isExist) { + // some child is not updated by UpdateSemantics. + continue; + } + if (childNode->IsVisible()) { + visible_num++; + last_visible_index = child_index; + } + if (childNode->isAccessibilityFocued) { + scrollCurrentIndex = child_index; + } + child_index++; + + // update parent and brother ptr + if (!childNode->parentNode || childNode->parentNode->id != id) { + childNode->parentNode = this; + childNode->parentId = this->id; + childNode->parentChanged = true; + } + if (prevNode) { + prevNode->nextNode = childNode; + } + childNode->previousNode = prevNode; + childNode->nextNode = nullptr; + prevNode = childNode; + + exist_children.push_back(childNode->id); + childNode->UpdateSelfRecursively(visitorId, absoluteTransform, needUpdate); + childNode->UpdateSelfElementInfo(); + + focusableInSubtree = focusableInSubtree || childNode->focusableInSubtree; + } + + if (exist_children != existChildrenInTraversalOrder) { + existChildrenInTraversalOrder = std::move(exist_children); + childrenChanged = true; + // FML_DLOG(DEBUG) << id << " node children num " + // << childrenInTraversalOrder.size() << " exist " + // << existChildrenInTraversalOrder.size(); + } + + // Update scroll info: it need the visible child node num. + if (scrollChildren != 0 && + (visible_num != scrollEndIndex - scrollIndex + 1 || scrollChanged)) { + scrollVisibleNum = visible_num; + scrollVisibleEndIndex = last_visible_index; + + scrollEndIndex = scrollIndex + visible_num - 1; + scrollChanged = true; + if (scrollIndex + visible_num > scrollChildren) { + FML_DLOG(WARNING) + << "UpdateSelfRecursively -> Scroll index is out of bounds " + << scrollIndex << " visibleNum" << visible_num << " children " + << scrollChildren; + } + if (!childrenInHitTestOrder.size()) { + FML_DLOG(WARNING) << "UpdateSelfRecursively -> Had scrollChildren but no " + "childrenInHitTestOrder"; + } + } +} + +void SemanticsNodeExtend::UpdateWithNode(flutter::SemanticsNode& node) { + isExist = true; + + if (id != node.id) { + id = node.id; + idChanged = true; + } + + // tooltip may use for componentType + previousLabel = label; + if (value != node.value || label != node.label || hint != node.hint || + tooltip != node.tooltip) { + value = std::move(node.value); + label = std::move(node.label); + hint = std::move(node.hint); + tooltip = std::move(node.tooltip); + if ((!label.empty() || !tooltip.empty() || !hint.empty()) && + componentType == OHWidgetName::kOtherWidgetName) { + componentType = OHWidgetName::kTextWidgetName; + } + contentChanged = true; + } + + previousFlags = flags; + if (flags != node.flags) { + flags = node.flags; + flagChanged = true; + } + + // IsFocusable need check flag and content + // We will add focus action in ActionsUpdate using IsFocusable. + previousActions = actions; + if (actions != node.actions) { + actions = node.actions; + actionChanged = true; + } + + if (textSelectionBase != node.textSelectionBase || + textSelectionExtent != node.textSelectionExtent) { + textSelectionBase = node.textSelectionBase; + textSelectionExtent = node.textSelectionExtent; + selectChanged = true; + } + + previousScrollPosition = scrollPosition; + if (scrollIndex != node.scrollIndex || + scrollChildren != node.scrollChildren) { + scrollPosition = node.scrollPosition; + scrollExtentMax = node.scrollExtentMax; + scrollExtentMin = node.scrollExtentMin; + scrollIndex = node.scrollIndex; + scrollChildren = node.scrollChildren; + scrollChanged = true; + // we need visible children num to update info. + } + + // childrenChanged is check in UpdateSelfRecursively + // we need know which node is not exist + childrenInTraversalOrder = std::move(node.childrenInTraversalOrder); + + rectChanged = ((rect != node.rect) || (transform != node.transform)); + if (rectChanged) { + rect = node.rect; + transform = node.transform; + } + + if (actionChanged || flagChanged || contentChanged || id == 0) { + OHOSComponentTypeUpdate(); + OHOSActionsUpdate(); + propertyChanged = true; + } + + maxValueLength = node.maxValueLength; + currentValueLength = node.currentValueLength; + platformViewId = node.platformViewId; + elevation = node.elevation; + thickness = node.thickness; + + valueAttributes = std::move(node.valueAttributes); + labelAttributes = std::move(node.labelAttributes); + + hintAttributes = std::move(node.hintAttributes); + increasedValue = std::move(node.increasedValue); + increasedValueAttributes = std::move(node.increasedValueAttributes); + decreasedValue = std::move(node.decreasedValue); + decreasedValueAttributes = std::move(node.decreasedValueAttributes); + textDirection = node.textDirection; + childrenInHitTestOrder = std::move(node.childrenInHitTestOrder); + customAccessibilityActions = std::move(node.customAccessibilityActions); + identifier = std::move(node.identifier); +} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/accessibility/ohos_semantics_node.h b/shell/platform/ohos/accessibility/ohos_semantics_node.h new file mode 100644 index 0000000000000000000000000000000000000000..2439d028a5f98cb40d43eeb9be130fc727e2ccab --- /dev/null +++ b/shell/platform/ohos/accessibility/ohos_semantics_node.h @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_ACCESSIBILITY_OHOS_SEMANTICS_NODE_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_ACCESSIBILITY_OHOS_SEMANTICS_NODE_H_ + +#include +#include +#include +#include +#include +#include +#include "flutter/lib/ui/semantics/semantics_node.h" + +namespace flutter { +typedef flutter::SemanticsFlags FLAGS_; +typedef flutter::SemanticsAction ACTIONS_; + +class OHWidgetName { + public: + static constexpr const char* kOtherWidgetName = "View"; + static constexpr const char* kTextWidgetName = "Text"; + static constexpr const char* kEditTextWidgetName = "TextInput"; + static constexpr const char* kEditMultilineTextWidgetName = "TextArea"; + static constexpr const char* kImageWidgetName = "Image"; + static constexpr const char* kScrollWidgetName = "Scroll"; + static constexpr const char* kButtonWidgetName = "Button"; + static constexpr const char* kLinkWidgetName = "Link"; + static constexpr const char* kSliderWidgetName = "Slider"; + static constexpr const char* kHeaderWidgetName = "Header"; + static constexpr const char* kRadioButtonWidgetName = "Radio"; + static constexpr const char* kCheckBoxWidgetName = "Checkbox"; + static constexpr const char* kSwitchWidgetName = "Toggle"; + static constexpr const char* kSeekbarWidgetName = "SeekBar"; + static constexpr const char* kRootWidgetName = "root"; +}; + +struct SemanticsNodeExtend : flutter::SemanticsNode { + static constexpr int32_t kFocusableFlags = + static_cast(FLAGS_::kHasCheckedState) | + static_cast(FLAGS_::kIsChecked) | + static_cast(FLAGS_::kIsSelected) | + static_cast(FLAGS_::kIsTextField) | + static_cast(FLAGS_::kIsFocused) | + static_cast(FLAGS_::kHasEnabledState) | + static_cast(FLAGS_::kIsEnabled) | + static_cast(FLAGS_::kIsInMutuallyExclusiveGroup) | + static_cast(FLAGS_::kHasToggledState) | + static_cast(FLAGS_::kIsToggled) | + static_cast(FLAGS_::kHasToggledState) | + static_cast(FLAGS_::kIsFocusable) | + static_cast(FLAGS_::kIsSlider); + + static constexpr int32_t kScrollableAction = + static_cast(ACTIONS_::kScrollLeft) | + static_cast(ACTIONS_::kScrollRight) | + static_cast(ACTIONS_::kScrollUp) | + static_cast(ACTIONS_::kScrollDown); + + // @TODO why? documents don't say this. + static constexpr int32_t kArkuiAccessibilityRootParentId = -2100000; + + bool hasUpdate = false; + bool hasInit = false; + bool childrenChanged = false; + bool scrollChanged = false; + bool selectChanged = false; + bool flagChanged = false; + bool actionChanged = false; + bool propertyChanged = false; + bool contentChanged = false; + bool rectChanged = false; + bool parentChanged = false; + bool idChanged = false; + bool isExist = false; + + bool performScrollAction = false; + bool performSelectAction = false; + bool focusableInSubtree = false; + bool isAccessibilityFocued = false; + int32_t previousFlags = 0; + int32_t previousActions = 0; + double previousScrollPosition = std::nan(""); + std::string previousLabel; + SemanticsNodeExtend* parentNode = nullptr; + SemanticsNodeExtend* previousNode = nullptr; + SemanticsNodeExtend* nextNode = nullptr; + std::vector childrenInTraversalOrderList; + std::vector existChildrenInTraversalOrder; + + SkRect absoluteRect; + SkM44 absoluteTransform; + int32_t scrollEndIndex = 0; + int32_t scrollCurrentIndex = -1; + int32_t scrollVisibleNum = 0; + int32_t scrollVisibleEndIndex = 0; + uint32_t parentId = 0; + + std::string contentString = ""; + ArkUI_AccessibilityElementInfo* elementInfoOHOS = nullptr; + const char* componentType = OHWidgetName::kOtherWidgetName; + std::vector ohActions; + + SemanticsNodeExtend() { + elementInfoOHOS = OH_ArkUI_CreateAccessibilityElementInfo(); + } + + ~SemanticsNodeExtend() { + OH_ArkUI_DestoryAccessibilityElementInfo(elementInfoOHOS); + } + + void FillElementInfo(ArkUI_AccessibilityElementInfo* info); + void UpdateSelfElementInfo(); + void FillElementInfoWithId(ArkUI_AccessibilityElementInfo* info); + void FillElementInfoWithProperty(ArkUI_AccessibilityElementInfo* info); + void FillElementInfoWithContent(ArkUI_AccessibilityElementInfo* info); + void FillElementInfoWithChildren(ArkUI_AccessibilityElementInfo* info); + void FillElementInfoWithScroll(ArkUI_AccessibilityElementInfo* info); + void FillElementInfoWithRect(ArkUI_AccessibilityElementInfo* info); + void FillElementInfoWithSelect(ArkUI_AccessibilityElementInfo* info); + void FillElementInfoWithParent(ArkUI_AccessibilityElementInfo* info); + + void OHOSActionsUpdate(); + void OHOSComponentTypeUpdate(); + + void UpdateWithNode(flutter::SemanticsNode& node); + void UpdateSelfRecursively(std::unordered_set& visitorId, + SkM44& fatherTransform, + bool needUpdat); + + bool HasPrevAction(SemanticsAction action) const { + return (previousActions & this->actions) != 0; + } + bool HasPrevFlag(SemanticsFlags flag) const { + return (previousFlags & this->flags) != 0; + } + + void setAbsoluteRect(float left, float top, float right, float bottom) { + absoluteRect.fLeft = left; + absoluteRect.fTop = top; + absoluteRect.fRight = right; + absoluteRect.fBottom = bottom; + } + + bool IsTextField() { return HasFlag(FLAGS_::kIsTextField); } + bool IsEditable() { return IsTextField() && !HasFlag(FLAGS_::kIsReadOnly); } + bool IsSlider() { return HasFlag(FLAGS_::kIsSlider); } + bool IsVisible() { return !HasFlag(FLAGS_::kIsHidden); } + bool IsCheckable() { + return HasFlag(FLAGS_::kHasCheckedState) || + HasFlag(FLAGS_::kHasToggledState); + } + bool IsChecked() { + return HasFlag(FLAGS_::kIsChecked) || HasFlag(FLAGS_::kIsToggled); + } + bool IsSelected() { return HasFlag(FLAGS_::kIsSelected); } + bool IsPassword() { + return HasFlag(FLAGS_::kIsTextField) && HasFlag(FLAGS_::kIsObscured); + } + bool IsEnabled() { + return !HasFlag(FLAGS_::kHasEnabledState) || HasFlag(FLAGS_::kIsEnabled); + } + bool IsClickable() { return HasAction(ACTIONS_::kTap); } + bool IsHasLongPress() { return HasAction(ACTIONS_::kLongPress); } + bool HasScrolled() { + return scrollPosition != std::nan("") && + previousScrollPosition != std::nan("") && + previousScrollPosition != scrollPosition; + } + bool HasChangedLabel() { + if (label.empty() && previousLabel.empty()) { + return false; + } + return label.empty() || previousLabel.empty() || label != previousLabel; + } + bool IsFocusable() { + if (HasFlag(FLAGS_::kScopesRoute)) { + return false; + } + if (HasFlag(FLAGS_::kIsFocusable)) { + return true; + } + if (IsPlatformViewNode()) { + return true; + } + if ((flags & kFocusableFlags) != 0) { + return true; + } + if ((actions & ~kScrollableAction) != 0) { + return true; + } + return !label.empty() || !value.empty() || !hint.empty(); + } + bool IsFocused() { return HasFlag(FLAGS_::kIsFocused); } + bool IsScrollable() { + return HasAction(ACTIONS_::kScrollLeft) || + HasAction(ACTIONS_::kScrollRight) || + HasAction(ACTIONS_::kScrollUp) || HasAction(ACTIONS_::kScrollDown); + } + + std::string GetHintText() { + std::string result = label; + if (result.length() != 0) { + if (hint.length() != 0) { + result += " ," + hint; + } + } else { + result = hint; + } + return result; + } + + std::string GetContents() { + std::string result = GetHintText(); + if (value.length() != 0) { + if (result.length() != 0) { + result = value + " ," + result; + } else { + result = value; + } + } + return result; + } +}; + +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_ACCESSIBILITY_OHOS_SEMANTICS_NODE_H_ \ No newline at end of file diff --git a/shell/platform/ohos/accessibility/ohos_semantics_tree.cpp b/shell/platform/ohos/accessibility/ohos_semantics_tree.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0f4394445d980536413286651ddbd350bfd1ac7b --- /dev/null +++ b/shell/platform/ohos/accessibility/ohos_semantics_tree.cpp @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * 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 "ohos_semantics_tree.h" +#include +#include +#include +#include +#include + +namespace flutter { + +SemanticsTree::~SemanticsTree() { + ClearSemanticsTree(); +} + +void SemanticsTree::RemoveNode(int32_t id) { + auto node = FindNodeById(id); + if (!node) { + return; + } + all_semantics_nodes_.erase(id); + + if (focused_node_ == node) { + ClearAccessibilityFocusNode(); + } + if (need_request_focused_node_ == node) { + need_request_focused_node_ = nullptr; + } + delete node; +} + +void SemanticsTree::ClearAccessibilityFocusNode() { + if (focused_node_) { + focused_node_->isAccessibilityFocued = false; + } + focused_node_ = nullptr; +} + +bool SemanticsTree::SetAccessibilityFocusNode(int32_t id) { + auto node = FindNodeById(id); + if (node) { + focused_node_ = node; + focused_node_->isAccessibilityFocued = true; + return true; + } else { + return false; + } +} + +std::vector SemanticsTree::UpdateWithNodes( + std::unordered_map& nodes) { + bool has_focus_node = false; + last_input_focused_node_ = input_focused_node_; + + for (auto& it : nodes) { + auto node = it.second; + SemanticsNodeExtend* nodeExt = GetOrAddNode(it.first); + nodeExt->UpdateWithNode(node); + if (it.first == 0) { + root_node_ = nodeExt; + } + if (!nodeExt->IsVisible()) { + continue; + } + if (nodeExt->IsFocused()) { + input_focused_node_ = nodeExt; + has_focus_node = true; + } + nodeExt->childrenInTraversalOrderList.clear(); + for (auto nodeId : nodeExt->childrenInTraversalOrder) { + nodeExt->childrenInTraversalOrderList.push_back(GetOrAddNode(nodeId)); + } + } + + std::unordered_set visitorId; + SkM44 transform; + if (root_node_) { + root_node_->UpdateSelfRecursively(visitorId, transform, false); + root_node_->UpdateSelfElementInfo(); + assert(!root_node_->IsFocusable()); + } + + std::unordered_set need_remove_ids; + for (auto& item : all_semantics_nodes_) { + if (visitorId.find(item.first) == visitorId.end()) { + need_remove_ids.insert(item.first); + } + } + + UpdateNextFocusWhenDisappear(need_remove_ids); + + for (auto id : need_remove_ids) { + RemoveNode(id); + } + + if (!has_focus_node) { + input_focused_node_ = nullptr; + } + + std::vector updatedNodes; + for (auto& it : nodes) { + SemanticsNodeExtend* nodeExt = FindNodeById(it.first); + if (nodeExt && nodeExt->hasUpdate) { + updatedNodes.push_back(nodeExt); + } + } + + return updatedNodes; +} + +bool SemanticsTree::UpdateNextFocusWhenDisappear( + std::unordered_set& need_remove_ids) { + // If the focused node disappears, we need to shift focus to a new node; + // otherwise, the accessibility focus green border will not update. + + bool focused_node_is_disappear = false; + if (focused_node_ && (need_remove_ids.count(focused_node_->id) != 0 || + !focused_node_->IsVisible())) { + focused_node_is_disappear = true; + } + bool request_focused_node_need_update = + !need_request_focused_node_ || !need_request_focused_node_->IsVisible(); + + if ((focused_node_is_disappear || need_find_focus_node_again_) && + request_focused_node_need_update) { + // if focused_node is not null, focused_node cannot be root and must have + // parent. + if (focused_node_) { + assert(focused_node_->parentNode != nullptr && + focused_node_ != root_node_); + } + + SemanticsNodeExtend* find_node = nullptr; + + if (focused_node_ && + need_remove_ids.count(focused_node_->parentNode->id) == 0) { + // father is exsit + // first find brother + if (focused_node_->nextNode) { + find_node = focused_node_->nextNode; + while (find_node != nullptr) { + if (find_node->isExist && find_node->IsFocusable() && + find_node->IsVisible() && + need_remove_ids.count(find_node->id) == 0) { + break; + } + find_node = find_node->nextNode; + } + } + + if (!find_node && focused_node_->previousNode) { + find_node = focused_node_->previousNode; + while (find_node != nullptr) { + if (find_node->isExist && find_node->IsFocusable() && + find_node->IsVisible() && + need_remove_ids.count(find_node->id) == 0) { + break; + } + find_node = find_node->previousNode; + } + } + + // second find parent's other children + if (!find_node) { + for (auto child : + focused_node_->parentNode->childrenInTraversalOrderList) { + if (child->isExist && child->IsFocusable() && child->IsVisible() && + need_remove_ids.count(child->id) == 0) { + find_node = child; + break; + } + } + } + } + + // last go ancestor + if (!find_node && focused_node_) { + find_node = focused_node_->parentNode; + while (find_node != nullptr && find_node->id != 0) { + if (find_node->IsFocusable() && find_node->IsVisible() && + need_remove_ids.count(find_node->id) == 0) { + break; + } + find_node = find_node->parentNode; + } + } + + // get root, then find the next focuabled node. + if (!find_node || find_node->id == 0) { + find_node = + FindNextFocusNode(0, ARKUI_ACCESSIBILITY_NATIVE_DIRECTION_FORWARD); + } + if (find_node) { + if (find_node->id != 0) { + need_request_focused_node_ = find_node; + focus_request_has_send_ = false; + need_find_focus_node_again_ = false; + return true; + } else { + // find root again--means it don't have a focusable node, request again. + need_find_focus_node_again_ = true; + return false; + } + } else { + return false; + } + } else { + return false; + } +} + +bool SemanticsTree::FillNodeInfo(SemanticsNodeExtend* node, + ArkUI_AccessibilityElementInfoList* list) { + assert(node->parentNode || node->id == 0); + auto info = OH_ArkUI_AddAndGetAccessibilityElementInfo(list); + if (info != nullptr) { + node->FillElementInfo(info); + } else { + FML_DLOG(ERROR) << "ohos_semantics_tree -> " + "OH_ArkUI_AddAndGetAccessibilityElementInfo -> " + "ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED"; + return false; + } + return true; +} + +SemanticsNodeExtend* SemanticsTree::FindFocusNode( + int32_t id, + ArkUI_AccessibilityFocusType focusType) { + SemanticsNodeExtend* return_node = nullptr; + switch (focusType) { + case ARKUI_ACCESSIBILITY_NATIVE_FOCUS_TYPE_INPUT: + return_node = input_focused_node_; + break; + case ARKUI_ACCESSIBILITY_NATIVE_FOCUS_TYPE_ACCESSIBILITY: + return_node = focused_node_; + break; + default: { + FML_DLOG(ERROR) << "ohos_semantics_tree -> FindFocusNode -> " + "ARKUI_ACCESSIBILITY_NATIVE_FOCUS_TYPE_INVALID"; + break; + } + } + + if (id == -1) { + return return_node; + } else { + auto temp_node = return_node; + // check if start id is the ancestor. + while (temp_node != nullptr) { + if (temp_node->id == id) { + return return_node; + } + temp_node = temp_node->parentNode; + } + } + + return nullptr; +} + +// Only nodes configured with the action +// ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_FORWARD/BACKWARD will +// calling the current function. +SemanticsNodeExtend* SemanticsTree::FindNextFocusNode( + int32_t id, + ArkUI_AccessibilityFocusMoveDirection direction) { + auto startNode = FindNodeById(id); + if (!startNode) { + FML_DLOG(ERROR) << "ohos_semantics_tree -> FindNextFocusNode -> " + "FindNodeById failed"; + return nullptr; + } + auto currentNode = startNode; + + while (true) { + SemanticsNodeExtend* returnNode = currentNode; + + // pick next node + switch (direction) { + case ARKUI_ACCESSIBILITY_NATIVE_DIRECTION_UP: { + if (currentNode->parentNode != nullptr) { + returnNode = currentNode->parentNode; + } + break; + } + case ARKUI_ACCESSIBILITY_NATIVE_DIRECTION_DOWN: { + if (!currentNode->childrenInTraversalOrderList.empty()) { + returnNode = currentNode->childrenInTraversalOrderList[0]; + } + break; + } + case ARKUI_ACCESSIBILITY_NATIVE_DIRECTION_LEFT: { + if (currentNode->previousNode != nullptr) { + returnNode = currentNode->previousNode; + } + break; + } + case ARKUI_ACCESSIBILITY_NATIVE_DIRECTION_RIGHT: { + if (currentNode->nextNode != nullptr) { + returnNode = currentNode->nextNode; + } + break; + } + case ARKUI_ACCESSIBILITY_NATIVE_DIRECTION_BACKWARD: { + if (currentNode->previousNode != nullptr) { + returnNode = currentNode->previousNode; + } else if (currentNode->parentNode != nullptr) { + returnNode = currentNode->parentNode; + } + break; + } + case ARKUI_ACCESSIBILITY_NATIVE_DIRECTION_FORWARD: { + SemanticsNodeExtend* candidateNode = nullptr; + for (auto childNode : currentNode->childrenInTraversalOrderList) { + if (childNode && childNode->isExist && + childNode->focusableInSubtree) { + candidateNode = childNode; + break; + } + } + if (candidateNode) { + returnNode = candidateNode; + } else if (currentNode->nextNode != nullptr) { + returnNode = currentNode->nextNode; + } + break; + } + default: { + FML_DLOG(ERROR) << "Invalid focus direction"; + return currentNode; + } + } + // We should not focus on root node or cannot find the next node + if (returnNode == root_node_ || returnNode == currentNode) { + return startNode; + } + + if (returnNode->IsFocusable()) { + return returnNode; + } else { + currentNode = returnNode; // enter the next iteration + } + } + + // Unreachable + return nullptr; +} + +bool SemanticsTree::FillNodesRecursive( + int32_t id, + const char* text, + ArkUI_AccessibilityElementInfoList* list) { + bool withText = (text != nullptr); + int fillNum = 0; + auto startNode = FindNodeById(id); + if (!startNode) { + return false; + } + std::queue q; + q.push(startNode); + bool retValue = true; + std::string cppStringText = withText ? std::string(text) : ""; + while (!q.empty()) { + auto currentNode = q.front(); + q.pop(); + if (currentNode) { + // In the SearchText process, we should precisely match the string of + // SetContent. + if (!withText || cppStringText == currentNode->contentString) { + retValue = FillNodeInfo(currentNode, list) && retValue; + fillNum++; + } + for (auto& childNode : currentNode->childrenInTraversalOrderList) { + if (childNode && childNode->isExist) { + q.push(childNode); + } + } + } + } + FML_DLOG(DEBUG) << "FillNodesRecursive " << id << " " << (text ? text : "") + << " fillNum " << fillNum; + return retValue; +} + +bool SemanticsTree::FillNodesWithSearchText( + int32_t id, + const char* text, + ArkUI_AccessibilityElementInfoList* list) { + return FillNodesRecursive(id, text, list); +} + +bool SemanticsTree::FillNodesWithSearch( + int32_t id, + ArkUI_AccessibilitySearchMode mode, + ArkUI_AccessibilityElementInfoList* list) { + auto startNode = FindNodeById(id); + if (!startNode) { + FML_DLOG(DEBUG) << "FillNodesWithSearch failed find " << id; + return false; + } + bool retValue = true; + // Currently, except for + // ARKUI_ACCESSIBILITY_NATIVE_SEARCH_MODE_PREFETCH_CURRENT and + // ARKUI_ACCESSIBILITY_NATIVE_SEARCH_MODE_PREFETCH_RECURSIVE_CHILDREN, the + // other ArkUI_AccessibilitySearchMode options are not invoked by the + // accessibility framework. + switch (mode) { + case ARKUI_ACCESSIBILITY_NATIVE_SEARCH_MODE_PREFETCH_CURRENT: { + retValue = FillNodeInfo(startNode, list) && retValue; + break; + } + case ARKUI_ACCESSIBILITY_NATIVE_SEARCH_MODE_PREFETCH_PREDECESSORS: { + auto parentNode = startNode->parentNode; + assert(parentNode != nullptr); + retValue = FillNodeInfo(parentNode, list) && retValue; + break; + } + case ARKUI_ACCESSIBILITY_NATIVE_SEARCH_MODE_PREFETCH_SIBLINGS: { + auto parentNode = startNode->parentNode; + for (auto& siblingNode : parentNode->childrenInTraversalOrderList) { + if (siblingNode && siblingNode->isExist) { + retValue = FillNodeInfo(siblingNode, list) && retValue; + } + } + break; + } + case ARKUI_ACCESSIBILITY_NATIVE_SEARCH_MODE_PREFETCH_CHILDREN: { + retValue = FillNodeInfo(startNode, list) && retValue; + for (auto& childNode : startNode->childrenInTraversalOrderList) { + if (childNode && childNode->isExist) { + retValue = FillNodeInfo(childNode, list) && retValue; + } + } + break; + } + // The results of the current search mode should include the info of the + // current node. The order of nodes should follow the level-order. + case ARKUI_ACCESSIBILITY_NATIVE_SEARCH_MODE_PREFETCH_RECURSIVE_CHILDREN: { + retValue = FillNodesRecursive(id, nullptr, list) && retValue; + break; + } + default: { + FML_DLOG(ERROR) << "ohos_semantics_tree -> FillNodesWithSearch -> " + "ArkUI_AccessibilitySearchMode Invalid"; + return false; + } + } + // FML_DLOG(DEBUG) << "FillNodesWithSearch " << id; + return retValue; +} + +SemanticsNodeExtend* SemanticsTree::FindNodeById(int32_t id) { + // redirect to root node + if (id == -1) { + id = 0; + } + if (all_semantics_nodes_.count(id) == 1) { + auto node = all_semantics_nodes_[id]; + if (node->id != id) { + // this node is some node's child but not given by UpdateSematics + return nullptr; + } else { + return node; + } + } else { + return nullptr; + } +} + +SemanticsNodeExtend* SemanticsTree::GetOrAddNode(int32_t id) { + SemanticsNodeExtend* semanticsNode = all_semantics_nodes_[id]; + if (semanticsNode == nullptr) { + semanticsNode = new SemanticsNodeExtend(); + all_semantics_nodes_[id] = semanticsNode; + } + return semanticsNode; +} + +void SemanticsTree::ClearSemanticsTree() { + for (auto it : all_semantics_nodes_) { + delete it.second; + } + all_semantics_nodes_.clear(); + root_node_ = nullptr; + focused_node_ = nullptr; + need_request_focused_node_ = nullptr; + focus_request_has_send_ = false; + need_find_focus_node_again_ = false; + input_focused_node_ = nullptr; + last_input_focused_node_ = nullptr; +} +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/accessibility/ohos_semantics_tree.h b/shell/platform/ohos/accessibility/ohos_semantics_tree.h new file mode 100644 index 0000000000000000000000000000000000000000..6440fbefc609f8bedbb95c92a5161fd23ac65e20 --- /dev/null +++ b/shell/platform/ohos/accessibility/ohos_semantics_tree.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_ACCESSIBILITY_OHOS_SEMANTICS_TREE_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_ACCESSIBILITY_OHOS_SEMANTICS_TREE_H_ + +#include +#include +#include +#include "ohos_semantics_node.h" + +namespace flutter { + +class SemanticsTree { + public: + SemanticsTree() = default; + ~SemanticsTree(); + + void ClearSemanticsTree(); + + SemanticsNodeExtend* FindNodeById(int32_t id); + SemanticsNodeExtend* GetOrAddNode(int32_t id); + void RemoveNode(int32_t id); + bool SetAccessibilityFocusNode(int32_t id); + void ClearAccessibilityFocusNode(); + + std::vector UpdateWithNodes( + std::unordered_map& nodes); + + SemanticsNodeExtend* FindFocusNode(int32_t id, + ArkUI_AccessibilityFocusType focusType); + SemanticsNodeExtend* FindNextFocusNode( + int32_t id, + ArkUI_AccessibilityFocusMoveDirection direction); + + bool FillNodesWithSearchText(int32_t id, + const char* text, + ArkUI_AccessibilityElementInfoList* list); + bool FillNodesWithSearch(int32_t id, + ArkUI_AccessibilitySearchMode mode, + ArkUI_AccessibilityElementInfoList* list); + SemanticsNodeExtend* GetRootNode() { return root_node_; } + bool UpdateNextFocusWhenDisappear( + std::unordered_set& need_remove_ids); + + // This flag is set after the event is sent, indicating the event has been + // dispatched, but the focus node has not updated yet. + bool focus_request_has_send_ = false; + // This flag indicates that the current focus node needs to be updated, but no + // focusable node has been found yet, requiring a retry later. + bool need_find_focus_node_again_ = false; + SemanticsNodeExtend* need_request_focused_node_ = nullptr; + SemanticsNodeExtend* focused_node_ = nullptr; + + private: + std::unordered_map all_semantics_nodes_; + SemanticsNodeExtend* root_node_ = nullptr; + SemanticsNodeExtend* input_focused_node_ = nullptr; + SemanticsNodeExtend* last_input_focused_node_ = nullptr; + + bool FillNodesRecursive(int32_t id, + const char* text, + ArkUI_AccessibilityElementInfoList* list); + bool FillNodeInfo(SemanticsNodeExtend* node, + ArkUI_AccessibilityElementInfoList* list); +}; + +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_ACCESSIBILITY_OHOS_SEMANTICS_TREE_H_ \ No newline at end of file diff --git a/shell/platform/ohos/config/BUILD.gn b/shell/platform/ohos/config/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..d8a2dd87c9a984b7392d1658c1fb80b0df9b6c68 --- /dev/null +++ b/shell/platform/ohos/config/BUILD.gn @@ -0,0 +1,15 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#import("//build/config/linux/pkg_config.gni") +import("//flutter/shell/platform/glfw/config.gni") diff --git a/shell/platform/ohos/context/ohos_context.cpp b/shell/platform/ohos/context/ohos_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..43058ed68bd0d8642cf10af625e1b9081b84e0ba --- /dev/null +++ b/shell/platform/ohos/context/ohos_context.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/context/ohos_context.h" + +namespace flutter { + +OHOSContext::OHOSContext(OHOSRenderingAPI rendering_api) + : rendering_api_(rendering_api) {} + +OHOSContext::~OHOSContext() { + if (main_context_) { + main_context_->releaseResourcesAndAbandonContext(); + } + if (impeller_context_) { + impeller_context_->Shutdown(); + FML_LOG(IMPORTANT) << "impeller context shutdown (Vulkan)."; + } +} + +OHOSRenderingAPI OHOSContext::RenderingApi() const { + return rendering_api_; +} + +bool OHOSContext::IsValid() const { + return true; +} + +void OHOSContext::SetMainSkiaContext( + const sk_sp& main_context) { + main_context_ = main_context; +} + +sk_sp OHOSContext::GetMainSkiaContext() const { + return main_context_; +} + +std::shared_ptr OHOSContext::GetImpellerContext() const { + return impeller_context_; +} + +void OHOSContext::SetImpellerContext( + const std::shared_ptr& context) { + impeller_context_ = context; +} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/context/ohos_context.h b/shell/platform/ohos/context/ohos_context.h new file mode 100644 index 0000000000000000000000000000000000000000..85c225549ea9a7bac2433966d909590213454f96 --- /dev/null +++ b/shell/platform/ohos/context/ohos_context.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_CONTEXT_OHOS_CONTEXT_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_CONTEXT_OHOS_CONTEXT_H_ + +#include "common/settings.h" +#include "flutter/fml/macros.h" +#include "flutter/impeller/renderer/context.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" + +namespace flutter { + +class OHOSContext { + public: + explicit OHOSContext(OHOSRenderingAPI rendering_api); + + virtual ~OHOSContext(); + + OHOSRenderingAPI RenderingApi() const; + + virtual bool IsValid() const; + + void SetMainSkiaContext(const sk_sp& main_context); + + sk_sp GetMainSkiaContext() const; + + //---------------------------------------------------------------------------- + /// @brief Accessor for the Impeller context associated with + /// HarmonyOSSurfaces and the raster thread. + /// + std::shared_ptr GetImpellerContext() const; + + protected: + /// Intended to be called from a subclass constructor after setup work for the + /// context has completed. + + // note: if this vulkan_dylib_ is in OHOSContextVulkanImpeller, + // it will get vulkan func addr not mapped crash when ~ContextVK() + // because ~ContextVK() is invoked later then ~NativeLibrary(). + fml::RefPtr vulkan_dylib_; + + void SetImpellerContext(const std::shared_ptr& context); + + private: + const OHOSRenderingAPI rendering_api_; + + // This is the Skia context used for on-screen rendering. + sk_sp main_context_; + + std::shared_ptr impeller_context_; + + FML_DISALLOW_COPY_AND_ASSIGN(OHOSContext); +}; + +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_CONTEXT_OHOS_CONTEXT_H_ diff --git a/shell/platform/ohos/flutter_embedding/.gitignore b/shell/platform/ohos/flutter_embedding/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..c837164ae81179285dbc1ecb343ec163171ae17e --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/.gitignore @@ -0,0 +1,12 @@ +/node_modules +**/oh_modules +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +local.properties +oh-package-lock.json5 diff --git a/shell/platform/ohos/flutter_embedding/AppScope/app.json5 b/shell/platform/ohos/flutter_embedding/AppScope/app.json5 new file mode 100755 index 0000000000000000000000000000000000000000..0b4a7d6f8a902ee0001e139801488216ddf4dc65 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/AppScope/app.json5 @@ -0,0 +1,25 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +{ + "app": { + "bundleName": "com.example.config", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/shell/platform/ohos/flutter_embedding/AppScope/resources/base/element/string.json b/shell/platform/ohos/flutter_embedding/AppScope/resources/base/element/string.json new file mode 100755 index 0000000000000000000000000000000000000000..816db6fc0504889a26e2c3a245aaf503157b8130 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "config" + } + ] +} diff --git a/shell/platform/ohos/flutter_embedding/AppScope/resources/base/media/app_icon.png b/shell/platform/ohos/flutter_embedding/AppScope/resources/base/media/app_icon.png new file mode 100755 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/shell/platform/ohos/flutter_embedding/AppScope/resources/base/media/app_icon.png differ diff --git a/shell/platform/ohos/flutter_embedding/build-profile.json5 b/shell/platform/ohos/flutter_embedding/build-profile.json5 new file mode 100755 index 0000000000000000000000000000000000000000..d531f8686b6d9e8687a24adb1b05cc681e87aaa0 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/build-profile.json5 @@ -0,0 +1,45 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.0(12)", + "runtimeOS": "HarmonyOS" + } + ], + "buildModeSet": [ + { + "name": "debug", + }, + { + "name": "release" + }, + { + "name": "profile" + } + ] + }, + "modules": [ + { + "name": "flutter", + "srcPath": "./flutter" + } + ] +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/.gitignore b/shell/platform/ohos/flutter_embedding/flutter/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..0f8d09bf4527a826ed8b8d3b53150dd5bfd420a9 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +oh-package-lock.json5 +BuildProfile.ets \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/build-profile.json5 b/shell/platform/ohos/flutter_embedding/flutter/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..b93937f294390a491253ad2a1f987a53b1d942d4 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/build-profile.json5 @@ -0,0 +1,53 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +{ + "apiType": "stageMode", + "buildOption": { + "sourceOption": { + "workers": [ + "./src/main/ets/embedding/engine/workers/PlatformChannelWorker.ets" + ] + }, + "nativeLib": { + "debugSymbol": { + "strip": false, + "exclude": [] + } + } + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + }, + "consumerFiles": ["./consumer-rules.txt"] + } + } + }, + ], + "targets": [ + { + "name": "default", + "runtimeOS": "HarmonyOS" + } + ] +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/consumer-rules.txt b/shell/platform/ohos/flutter_embedding/flutter/consumer-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..33b1039af6ed4432561d6763aeec26eb4629336c --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/consumer-rules.txt @@ -0,0 +1,4 @@ +# flutter_ohos 在混淆时需要保留的代码 +-keep-property-name +flutter +native* diff --git a/shell/platform/ohos/flutter_embedding/flutter/hvigorfile.ts b/shell/platform/ohos/flutter_embedding/flutter/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..eb1f1d089d8fbdcd5ea7af33ecb70f3c8b5bdfce --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/hvigorfile.ts @@ -0,0 +1,17 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +export { harTasks } from '@ohos/hvigor-ohos-plugin'; \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/index.ets b/shell/platform/ohos/flutter_embedding/flutter/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..e0290dd966ad722312e124f1f6bb7c32a68d3f49 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/index.ets @@ -0,0 +1,119 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +export { default as FlutterInjector } from './src/main/ets/FlutterInjector'; +export { default as FlutterPluginRegistry } from './src/main/ets/app/FlutterPluginRegistry'; +export { default as FlutterComponent } from './src/main/ets/component/FlutterComponent'; +export { default as FlutterEngine } from './src/main/ets/embedding/engine/FlutterEngine'; +export { default as FlutterEngineCache } from './src/main/ets/embedding/engine/FlutterEngineCache'; +export { default as FlutterEngineConnectionRegistry } from './src/main/ets/embedding/engine/FlutterEngineConnectionRegistry'; +export { default as FlutterEngineGroup } from './src/main/ets/embedding/engine/FlutterEngineGroup'; +export { default as FlutterEnginePreload } from './src/main/ets/embedding/engine/FlutterEnginePreload'; +export { default as FlutterEngineGroupCache } from './src/main/ets/embedding/engine/FlutterEngineGroupCache'; +export { default as FlutterNapi } from './src/main/ets/embedding/engine/FlutterNapi'; +export * from './src/main/ets/embedding/engine/FlutterOverlaySurface'; +export { default as FlutterShellArgs } from './src/main/ets/embedding/engine/FlutterShellArgs'; +export { default as DartExecutor } from './src/main/ets/embedding/engine/dart/DartExecutor'; +export * from './src/main/ets/embedding/engine/dart/DartMessenger'; +export * from './src/main/ets/embedding/engine/dart/PlatformMessageHandler'; +export { default as ApplicationInfoLoader } from './src/main/ets/embedding/engine/loader/ApplicationInfoLoader'; +export { default as FlutterApplicationInfo } from './src/main/ets/embedding/engine/loader/FlutterApplicationInfo'; +export { default as FlutterLoader } from './src/main/ets/embedding/engine/loader/FlutterLoader'; +export * from './src/main/ets/embedding/engine/mutatorsstack/FlutterMutatorView'; +export * from './src/main/ets/embedding/engine/mutatorsstack/FlutterMutatorsStack'; +export * from './src/main/ets/embedding/engine/plugins/FlutterPlugin'; +export { default as PluginRegistry } from './src/main/ets/embedding/engine/plugins/PluginRegistry'; +export { default as AbilityAware } from './src/main/ets/embedding/engine/plugins/ability/AbilityAware'; +export { default as AbilityControlSurface } from './src/main/ets/embedding/engine/plugins/ability/AbilityControlSurface'; +export * from './src/main/ets/embedding/engine/plugins/ability/AbilityPluginBinding'; +export * from './src/main/ets/embedding/engine/renderer/FlutterRenderer'; +export * from './src/main/ets/embedding/engine/renderer/FlutterUiDisplayListener'; +export { default as AccessibilityChannel } from './src/main/ets/embedding/engine/systemchannels/AccessibilityChannel'; +export { default as KeyEventChannel } from './src/main/ets/embedding/engine/systemchannels/KeyEventChannel'; +export { default as LifecycleChannel } from './src/main/ets/embedding/engine/systemchannels/LifecycleChannel'; +export { default as LocalizationChannel } from './src/main/ets/embedding/engine/systemchannels/LocalizationChannel'; +export { default as MouseCursorChannel } from './src/main/ets/embedding/engine/systemchannels/MouseCursorChannel'; +export { default as NavigationChannel } from './src/main/ets/embedding/engine/systemchannels/NavigationChannel'; +export { default as PlatformChannel } from './src/main/ets/embedding/engine/systemchannels/PlatformChannel'; +export { default as PlatformViewsChannel } from './src/main/ets/embedding/engine/systemchannels/PlatformViewsChannel'; +export { default as RestorationChannel } from './src/main/ets/embedding/engine/systemchannels/RestorationChannel'; +export { default as SettingsChannel } from './src/main/ets/embedding/engine/systemchannels/SettingsChannel'; +export { default as SystemChannel } from './src/main/ets/embedding/engine/systemchannels/SystemChannel'; +export { default as TestChannel } from './src/main/ets/embedding/engine/systemchannels/TestChannel'; +export { default as TextInputChannel } from './src/main/ets/embedding/engine/systemchannels/TextInputChannel'; +export { default as ExclusiveAppComponent } from './src/main/ets/embedding/ohos/ExclusiveAppComponent'; +export * from './src/main/ets/embedding/ohos/FlutterAbility'; +export * from './src/main/ets/embedding/ohos/FlutterAbilityAndEntryDelegate'; +export { default as FlutterAbilityLaunchConfigs } from './src/main/ets/embedding/ohos/FlutterAbilityLaunchConfigs'; +export { default as FlutterEngineConfigurator } from './src/main/ets/embedding/ohos/FlutterEngineConfigurator'; +export { default as FlutterEngineProvider } from './src/main/ets/embedding/ohos/FlutterEngineProvider'; +export { default as FlutterEntry } from './src/main/ets/embedding/ohos/FlutterEntry'; +export { default as FlutterManager, DragDropCallback as DragDropCallback } from './src/main/ets/embedding/ohos/FlutterManager'; +export * from './src/main/ets/embedding/ohos/FlutterPage'; +export { default as KeyboardManager } from './src/main/ets/embedding/ohos/KeyboardManager'; +export { default as OhosTouchProcessor } from './src/main/ets/embedding/ohos/OhosTouchProcessor'; +export { default as Settings } from './src/main/ets/embedding/ohos/Settings'; +export * from './src/main/ets/embedding/ohos/TouchEventTracker'; +export { default as WindowInfoRepositoryCallbackAdapterWrapper } from './src/main/ets/embedding/ohos/WindowInfoRepositoryCallbackAdapterWrapper'; +export { default as PlatformPlugin } from './src/main/ets/plugin/PlatformPlugin'; +export { default as BasicMessageChannel, Reply } from './src/main/ets/plugin/common/BasicMessageChannel'; +export { default as BinaryCodec } from './src/main/ets/plugin/common/BinaryCodec'; +export * from './src/main/ets/plugin/common/BinaryMessenger'; +export { default as EventChannel } from './src/main/ets/plugin/common/EventChannel'; +export { default as FlutterException } from './src/main/ets/plugin/common/FlutterException'; +export { default as JSONMessageCodec } from './src/main/ets/plugin/common/JSONMessageCodec'; +export { default as JSONMethodCodec } from './src/main/ets/plugin/common/JSONMethodCodec'; +export { default as MessageCodec } from './src/main/ets/plugin/common/MessageCodec'; +export { default as MethodCall } from './src/main/ets/plugin/common/MethodCall'; +export * from './src/main/ets/plugin/common/MethodChannel'; +export { default as MethodChannel } from './src/main/ets/plugin/common/MethodChannel'; +export { default as MethodCodec } from './src/main/ets/plugin/common/MethodCodec'; +export { default as BackgroundBasicMessageChannel } from './src/main/ets/plugin/common/BackgroundBasicMessageChannel'; +export { default as BackgroundMethodChannel} from './src/main/ets/plugin/common/BackgroundMethodChannel' +export { default as SendableBinaryCodec} from './src/main/ets/plugin/common/SendableBinaryCodec' +export { default as SendableJSONMessageCodec} from './src/main/ets/plugin/common/SendableJSONMessageCodec' +export { default as SendableJSONMethodCodec} from './src/main/ets/plugin/common/SendableJSONMethodCodec' +export { default as SendableMessageHandler} from './src/main/ets/plugin/common/SendableMessageHandler' +export { default as SendableMethodCallHandler} from './src/main/ets/plugin/common/SendableMethodCallHandler' +export { default as SendableMethodCodec} from './src/main/ets/plugin/common/SendableMethodCodec' +export { default as SendableStandardMessageCodec } from './src/main/ets/plugin/common/SendableStandardMessageCodec'; +export { default as SendableStandardMethodCodec } from './src/main/ets/plugin/common/SendableStandardMethodCodec'; +export { default as SendableStringCodec} from './src/main/ets/plugin/common/SendableStringCodec' +export { default as StandardMessageCodec } from './src/main/ets/plugin/common/StandardMessageCodec'; +export { default as StandardMethodCodec } from './src/main/ets/plugin/common/StandardMethodCodec'; +export { default as StringCodec } from './src/main/ets/plugin/common/StringCodec'; +export * from './src/main/ets/plugin/editing/ListenableEditingState'; +export * from './src/main/ets/plugin/editing/TextEditingDelta'; +export { default as TextInputPlugin } from './src/main/ets/plugin/editing/TextInputPlugin'; +export { default as LocalizationPlugin } from './src/main/ets/plugin/localization/LocalizationPlugin'; +export { default as MouseCursorPlugin } from './src/main/ets/plugin/mouse/MouseCursorPlugin'; +export { default as PlatformView } from './src/main/ets/plugin/platform/PlatformView'; +export { default as PlatformViewFactory } from './src/main/ets/plugin/platform/PlatformViewFactory'; +export { default as PlatformViewRegistry } from './src/main/ets/plugin/platform/PlatformViewRegistry'; +export { default as PlatformViewRegistryImpl } from './src/main/ets/plugin/platform/PlatformViewRegistryImpl'; +export * from './src/main/ets/plugin/platform/PlatformViewWrapper'; +export { default as PlatformViewsController } from './src/main/ets/plugin/platform/PlatformViewsController'; +export * from './src/main/ets/view/FlutterCallbackInformation'; +export { default as FlutterRunArguments } from './src/main/ets/view/FlutterRunArguments'; +export * from './src/main/ets/view/FlutterView'; +export * from './src/main/ets/view/TextureRegistry'; +export * from './src/main/ets/util/ByteBuffer'; +export { default as Log } from './src/main/ets/util/Log'; +export { default as MessageChannelUtils } from './src/main/ets/util/MessageChannelUtils'; +export { default as PathUtils } from './src/main/ets/util/PathUtils'; +export { default as StringUtils } from './src/main/ets/util/StringUtils'; +export { default as ToolUtils } from './src/main/ets/util/ToolUtils'; +export * from './src/main/ets/util/TraceSection'; +export { default as Any } from './src/main/ets/plugin/common/Any'; diff --git a/shell/platform/ohos/flutter_embedding/flutter/obfuscation-rules.txt b/shell/platform/ohos/flutter_embedding/flutter/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..eb5c766dfc6374b931f6a57b652b5f7675765da1 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope diff --git a/shell/platform/ohos/flutter_embedding/flutter/oh-package.json5 b/shell/platform/ohos/flutter_embedding/flutter/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..39bbaa64c01def1568782d0af4659f2391b78fad --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/oh-package.json5 @@ -0,0 +1,27 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +{ + "license": "Apache-2.0", + "author": "", + "name": "@ohos/flutter_ohos", + "description": "The embedder of flutter in ohos.", + "main": "index.ets", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": { + "@types/libflutter.so": "file:./src/main/cpp/types/libflutter" + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/index.d.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/index.d.ets new file mode 100644 index 0000000000000000000000000000000000000000..1a88837bc99e954552b24b2169b265d4460c9edc --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/index.d.ets @@ -0,0 +1,184 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import common from '@ohos.app.ability.common'; +import resourceManager from '@ohos.resourceManager'; +import image from '@ohos.multimedia.image'; +import FlutterNapi from '../../../ets/embedding/engine/FlutterNapi'; +import { ByteBuffer } from '../../../ets/util/ByteBuffer'; +import { FlutterCallbackInformation } from '../../../ets/view/FlutterCallbackInformation'; + +/** + * 设置刷新率 + */ +export const nativeUpdateRefreshRate: ( + ate: number +) => void; + +/* + * 初始化SkFontMgr::RefDefault() + */ +export const nativePrefetchDefaultFontManager: () => void; + +export const nativeCheckAndReloadFont: (nativeShellHolderId: number) => void; + +export const nativeUpdateSize: ( + width: number, + height: number +) => void; + +export const nativeUpdateDensity: ( + densityPixels: number +) => void; + +/** + * 初始化dart vm和flutter engine + */ +export const nativeInit: ( + context: common.Context, + args: Array, + bundlePath: string, + appStoragePath: string, + engineCachesPath: string, + initTimeMillis: number, + productModel: string +) => number | null; + +export const nativeAttach: (napi: FlutterNapi) => number; + +export const nativeSpawn: ( + nativeSpawningShellId: number | null, + entrypointFunctionName: string, + pathToEntrypointFunction: string, + initialRoute: string, + entrypointArgs: Array, + napi: FlutterNapi +) => number; + +export const nativeRunBundleAndSnapshotFromLibrary: ( + nativeShellHolderId: number, + bundlePath: string, + entrypointFunctionName: string, + pathToEntrypointFunction: string, + assetManager: resourceManager.ResourceManager, + entrypointArgs: Array +) => void; + +//Send a data-carrying response to a platform message received from Dart. +export const nativeInvokePlatformMessageResponseCallback: (nativeShellHolderId: number, responseId: number, message: ArrayBuffer, position: number) => void; + +// Send an empty response to a platform message received from Dart. +export const nativeInvokePlatformMessageEmptyResponseCallback: (nativeShellHolderId: number, responseId: number) => void; + +// Send a data-carrying platform message to Dart. +export const nativeDispatchPlatformMessage: (nativeShellHolderId: number, channel: String, message: ArrayBuffer, position: number, responseId: number) => void; + +// Send an empty platform message to Dart. +export const nativeDispatchEmptyPlatformMessage: (nativeShellHolderId: number, channel: String, responseId: number) => void; + +export const nativeSetViewportMetrics: (nativeShellHolderId: number, devicePixelRatio: number, physicalWidth: number + , physicalHeight: number, physicalPaddingTop: number, physicalPaddingRight: number + , physicalPaddingBottom: number, physicalPaddingLeft: number, physicalViewInsetTop: number + , physicalViewInsetRight: number, physicalViewInsetBottom: number, physicalViewInsetLeft: number + , systemGestureInsetTop: number, systemGestureInsetRight: number, systemGestureInsetBottom: number + , systemGestureInsetLeft: number, physicalTouchSlop: number, displayFeaturesBounds: Array + , displayFeaturesType: Array, displayFeaturesState: Array) => void; + +export const nativeGetSystemLanguages: (nativeShellHolderId: number, languages: Array) => void; + +export const nativeXComponentAttachFlutterEngine: (xcomponentId: string, nativeShellHolderId: number) => void; + +export const nativeXComponentPreDraw: (xcomponentId: string, nativeShellHolderId: number, width: number, height: number) => void; + +export const nativeXComponentDetachFlutterEngine: (xcomponentId: string, nativeShellHolderId: number) => void; + +export const nativeXComponentDispatchMouseWheel: (nativeShellHolderId: number, + xcomponentId: string, + eventType: string, + fingerId: number, + globalX: number, + globalY: number, + offsetY: number, + timestamp: number + ) => void; + + +/** + * Detaches flutterNapi和engine之间的关联 + * 这个方法执行前提是flutterNapi已经和engine关联 + */ +export const nativeDestroy: ( + nativeShellHolderId: number +) => void; + + +export const nativeUnregisterTexture: (nativeShellHolderId: number, textureId: number) => void; + +export const nativeRegisterPixelMap: (nativeShellHolderId: number, textureId: number, pixelMap: PixelMap) => void; + +export const nativeSetTextureBackGroundPixelMap: (nativeShellHolderId: number, textureId: number, pixelMap: PixelMap) => void; + +export const nativeRegisterTexture: (nativeShellHolderId: number, textureId: number) => number; + +export const nativeGetTextureWindowId: (nativeShellHolderId: number, textureId: number) => number; + +export const nativeSetExternalNativeImage: (nativeShellHolderId: number, textureId: number, native_image: number) => number; + +export const nativeResetExternalTexture: (nativeShellHolderId: number, textureId: number, need_surfaceId: boolean) => number; + +export const nativeEncodeUtf8: (str: string) => Uint8Array; + +export const nativeDecodeUtf8: (array: Uint8Array) => string; + +export const nativeSetTextureBufferSize: (nativeShellHolderId: number, textureId: number, width: number, height: number) => void; + +export const nativeNotifyTextureResizing: (nativeShellHolderId: number, textureId: number, width: number, height: number) => void; + +export const nativeEnableFrameCache: (nativeShellHolderId: number, enable: boolean) => void; + +export const nativeLookupCallbackInformation: (callback: FlutterCallbackInformation, handler: number) => number; + +export const nativeUnicodeIsEmoji: (code: number) => number; + +export const nativeUnicodeIsEmojiModifier: (code: number) => number; + +export const nativeUnicodeIsEmojiModifierBase: (code: number) => number; + +export const nativeUnicodeIsVariationSelector: (code: number) => number; + +export const nativeUnicodeIsRegionalIndicatorSymbol: (code: number) => number; + +/** + * accessibiltyChannel中的 + */ +export const nativeSetAccessibilityFeatures: (accessibilityFeatureFlags: number, responseId: number) => void; + +export const nativeAccessibilityStateChange: (nativeShellHolderId: number, state: Boolean) => void; + +export const nativeAccessibilityAnnounce: (nativeShellHolderId: number, message: string) => void; + +export const nativeAccessibilityOnTap: (nativeShellHolderId: number, nodeId: number) => void; + +export const nativeAccessibilityOnLongPress: (nativeShellHolderId: number, nodeId: number) => void; + +export const nativeAccessibilityOnTooltip: (nativeShellHolderId: number, message: string) => void; + +export const nativeSetSemanticsEnabled: (nativeShellHolderId: number, enabled: boolean) => void; + +export const nativeSetFontWeightScale: (nativeShellHolderId: number, fontWeightScale: number) => void; + +export const nativeSetFlutterNavigationAction: (nativeShellHolderId: number, isNavigate: boolean) => void; + +export const nativeUpdateCurrentXComponentId: (xcomponent_id: string) => void; \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/oh-package.json5 b/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..5d1822ef86535e3d92242c5cb99af8dd46b2204f --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/oh-package.json5 @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +{ + "name": "libflutter.so", + "types": "./index.d.ets", + "version": "", + "description": "Please describe the basic information." +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/FlutterInjector.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/FlutterInjector.ets new file mode 100644 index 0000000000000000000000000000000000000000..83dccac244239e468e6cffc7b989918fe7bd5036 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/FlutterInjector.ets @@ -0,0 +1,58 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import FlutterNapi from './embedding/engine/FlutterNapi'; +import FlutterLoader from './embedding/engine/loader/FlutterLoader'; + +/** + * flutter相关主要类的单例持有,帮助实现自身和其他类的实例化管理 + */ +export default class FlutterInjector { + private static instance: FlutterInjector; + private flutterLoader: FlutterLoader; + private preloadFlutterNapi: FlutterNapi | null = null; + + static getInstance(): FlutterInjector { + if (FlutterInjector.instance == null) { + FlutterInjector.instance = new FlutterInjector(); + } + return FlutterInjector.instance; + } + + /** + * 初始化 + */ + private constructor() { + this.flutterLoader = new FlutterLoader(new FlutterNapi()); + } + + getFlutterLoader(): FlutterLoader { + return this.flutterLoader; + } + + getFlutterNapi(): FlutterNapi { + if (this.preloadFlutterNapi) { + let retFlutterNapi = this.preloadFlutterNapi; + this.preloadFlutterNapi = null; + return retFlutterNapi; + } + return new FlutterNapi(); + } + + getPreloadFlutterNapi(): FlutterNapi { + this.preloadFlutterNapi = new FlutterNapi(); + return this.preloadFlutterNapi; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/app/FlutterPluginRegistry.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/app/FlutterPluginRegistry.ets new file mode 100644 index 0000000000000000000000000000000000000000..3b41f4c2b253387c3a754d570b9e8e82d173fe60 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/app/FlutterPluginRegistry.ets @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import { FlutterView } from '../view/FlutterView'; +import common from '@ohos.app.ability.common'; +import PlatformViewController from '../plugin/platform/PlatformViewsController' + +export default class FlutterPluginRegistry { + private mPlatformViewsController: PlatformViewController; + private mFlutterView: FlutterView | null = null; + private mContext: common.Context | null = null; + + constructor() { + this.mPlatformViewsController = new PlatformViewController(); + this.mFlutterView = null; + this.mContext = null; + } + + attach(flutterView: FlutterView, context: common.Context): void { + this.mFlutterView = flutterView; + this.mContext = context; + } + + detach(): void { + this.mPlatformViewsController.detach(); + this.mPlatformViewsController.onDetachedFromNapi(); + this.mFlutterView = null; + this.mContext = null; + } + + destroy(): void { + this.mPlatformViewsController.onDetachedFromNapi(); + } + + onPreEngineRestart(): void { + this.mPlatformViewsController.onPreEngineRestart(); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/component/FlutterComponent.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/component/FlutterComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..07497e672b21c51727c1a45166504e19603e4063 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/component/FlutterComponent.ets @@ -0,0 +1,32 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +/** + * 基础component,还未封装,看情况是否使用 + */ +@Component +export default struct FlutterComponent { + build() { + Row() { + Column() { + Text("xxx") + .fontSize(50) + .fontWeight(FontWeight.Bold) + } + .width('100%') + } + .height('100%') + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/component/XComponentStruct.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/component/XComponentStruct.ets new file mode 100644 index 0000000000000000000000000000000000000000..f14da6f19f91b65abed4d9825e30f158d35978ed --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/component/XComponentStruct.ets @@ -0,0 +1,58 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import Any from '../plugin/common/Any'; +import ApplicationInfoLoader from '../embedding/engine/loader/ApplicationInfoLoader'; + +import { BuilderParams, DVModelParameters } from '../view/DynamicView/dynamicView'; + +@Component +struct XComponentStruct { + private context: Any; + private applicationInfo = ApplicationInfoLoader.load(getContext()); + dvModelParams: DVModelParameters = new DVModelParameters(); + + build() { + // todo OS解决默认背景色后可以移除冗余重复代码,仅保留差异的backgroundColor属性条件配置 + if (this.applicationInfo.isDebugMode) { + XComponent({ + id: (this.dvModelParams as Record)["xComponentId"], + type: XComponentType.TEXTURE, + libraryname: 'flutter' + }) + .onLoad((context) => { + this.context = context; + }) + .onDestroy(() => { + }) + .backgroundColor(Color.White) + } else { + XComponent({ + id: (this.dvModelParams as Record)["xComponentId"], + type: XComponentType.TEXTURE, + libraryname: 'flutter' + }) + .onLoad((context) => { + this.context = context; + }) + .onDestroy(() => { + }) + } + } +} + +@Builder +export function BuildXComponentStruct(buildParams: BuilderParams) { + XComponentStruct({ dvModelParams: buildParams.params }); +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngine.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngine.ets new file mode 100644 index 0000000000000000000000000000000000000000..7c68bdd4eda4a74a3c2cb47abf7d31ac2990c8a5 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngine.ets @@ -0,0 +1,294 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import LifecycleChannel from './systemchannels/LifecycleChannel'; +import DartExecutor, { DartEntrypoint } from './dart/DartExecutor'; +import FlutterShellArgs from './FlutterShellArgs'; +import FlutterInjector from '../../FlutterInjector'; +import FlutterLoader from './loader/FlutterLoader'; +import common from '@ohos.app.ability.common'; +import resourceManager from '@ohos.resourceManager'; +import FlutterNapi from './FlutterNapi'; +import NavigationChannel from './systemchannels/NavigationChannel'; +import Log from '../../util/Log'; +import TestChannel from './systemchannels/TestChannel' +import FlutterEngineConnectionRegistry from './FlutterEngineConnectionRegistry'; +import PluginRegistry from './plugins/PluginRegistry'; +import AbilityControlSurface from './plugins/ability/AbilityControlSurface'; +import TextInputChannel from './systemchannels/TextInputChannel'; +import TextInputPlugin from '../../plugin/editing/TextInputPlugin'; +import PlatformChannel from './systemchannels/PlatformChannel'; +import SystemChannel from './systemchannels/SystemChannel'; +import MouseCursorChannel from './systemchannels/MouseCursorChannel'; +import RestorationChannel from './systemchannels/RestorationChannel'; +import LocalizationChannel from './systemchannels/LocalizationChannel'; +import AccessibilityChannel from './systemchannels/AccessibilityChannel'; +import LocalizationPlugin from '../../plugin/localization/LocalizationPlugin' +import SettingsChannel from './systemchannels/SettingsChannel'; +import PlatformViewsController from '../../plugin/platform/PlatformViewsController'; +import { FlutterRenderer } from './renderer/FlutterRenderer'; + +const TAG = "FlutterEngine"; + +/** + * 操作FlutterEngin相关 + */ +export default class FlutterEngine implements EngineLifecycleListener { + private engineLifecycleListeners = new Set(); + dartExecutor: DartExecutor; + private flutterLoader: FlutterLoader; + private assetManager: resourceManager.ResourceManager; + //channel定义 + private lifecycleChannel: LifecycleChannel | null = null; + private navigationChannel: NavigationChannel | null = null; + private textInputChannel: TextInputChannel | null = null; + private testChannel: TestChannel | null = null; + private platformChannel: PlatformChannel | null = null; + private systemChannel: SystemChannel | null = null; + private mouseCursorChannel: MouseCursorChannel | null = null; + private restorationChannel: RestorationChannel | null = null; + private accessibilityChannel: AccessibilityChannel | null = null; + private localeChannel: LocalizationChannel | null = null; + private flutterNapi: FlutterNapi; + private renderer: FlutterRenderer; + private pluginRegistry: FlutterEngineConnectionRegistry | null = null; + private textInputPlugin: TextInputPlugin | null = null; + private localizationPlugin: LocalizationPlugin | null = null; + private settingsChannel: SettingsChannel | null = null; + private platformViewsController: PlatformViewsController; + + /** + * 需要初始化的工作: + * 1、初始化DartExecutor + * 2、初始化所有channel + * 3、初始化plugin + * 4、初始化flutterLoader + * 5、初始化flutterNapi + * 6、engineLifecycleListeners + */ + constructor(context: common.Context, flutterLoader: FlutterLoader | null, flutterNapi: FlutterNapi | null, + platformViewsController: PlatformViewsController | null) { + const injector: FlutterInjector = FlutterInjector.getInstance(); + if (flutterNapi == null) { + flutterNapi = FlutterInjector.getInstance().getFlutterNapi(); + } + this.flutterNapi = flutterNapi; + this.assetManager = context.resourceManager; + + this.dartExecutor = new DartExecutor(this.flutterNapi, this.assetManager); + this.dartExecutor.onAttachedToNAPI(); + + if (flutterLoader == null) { + flutterLoader = injector.getFlutterLoader(); + } + this.flutterLoader = flutterLoader; + + this.renderer = new FlutterRenderer(this.flutterNapi); + + if (platformViewsController == null) { + platformViewsController = new PlatformViewsController(); + } + this.platformViewsController = platformViewsController; + this.platformViewsController.attach(context, this.renderer, this.dartExecutor); + } + + init(context: common.Context, dartVmArgs: Array | null, waitForRestorationData: boolean) { + if (!this.flutterNapi.isAttached()) { + this.flutterLoader.startInitialization(context) + this.flutterLoader.ensureInitializationComplete(dartVmArgs); + } + //channel初始化 + this.lifecycleChannel = new LifecycleChannel(this.dartExecutor); + this.navigationChannel = new NavigationChannel(this.dartExecutor); + this.textInputChannel = new TextInputChannel(this.dartExecutor); + this.testChannel = new TestChannel(this.dartExecutor); + this.platformChannel = new PlatformChannel(this.dartExecutor); + this.systemChannel = new SystemChannel(this.dartExecutor); + this.mouseCursorChannel = new MouseCursorChannel(this.dartExecutor); + this.restorationChannel = new RestorationChannel(this.dartExecutor, waitForRestorationData); + this.settingsChannel = new SettingsChannel(this.dartExecutor); + this.localeChannel = new LocalizationChannel(this.dartExecutor); + this.accessibilityChannel = new AccessibilityChannel(this.dartExecutor, this.flutterNapi); + this.flutterNapi.addEngineLifecycleListener(this); + this.localizationPlugin = new LocalizationPlugin(context, this.localeChannel); + + // It should typically be a fresh, unattached NAPI. But on a spawned engine, the NAPI instance + // is already attached to a native shell. In that case, the Java FlutterEngine is created around + // an existing shell. + if (!this.flutterNapi.isAttached()) { + this.attachToNapi(); + } + this.flutterNapi.setLocalizationPlugin(this.localizationPlugin); + + this.pluginRegistry = + new FlutterEngineConnectionRegistry(context.getApplicationContext(), this, this.flutterLoader); + this.localizationPlugin.sendLocaleToFlutter(); + Log.d(TAG, "Call init finished.") + } + + private attachToNapi(): void { + Log.d(TAG, "Attaching to NAPI."); + this.flutterNapi.attachToNative(); + if (!this.isAttachedToNapi()) { + throw new Error("FlutterEngine failed to attach to its native Object reference."); + } + } + + spawn(context: common.Context, + dartEntrypoint: DartEntrypoint, + initialRoute: string, + dartEntrypointArgs: Array, + platformViewsController: PlatformViewsController, + waitForRestorationData: boolean) { + if (!this.isAttachedToNapi()) { + throw new Error( + "Spawn can only be called on a fully constructed FlutterEngine"); + } + + const newFlutterNapi = + this.flutterNapi.spawn( + dartEntrypoint.dartEntrypointFunctionName, + dartEntrypoint.dartEntrypointLibrary, + initialRoute, + dartEntrypointArgs); + const flutterEngine = new FlutterEngine( + context, + null, + newFlutterNapi, + platformViewsController + ); + flutterEngine.init(context, null, waitForRestorationData) + return flutterEngine + } + + private isAttachedToNapi(): boolean { + return this.flutterNapi.isAttached(); + } + + processPendingMessages() { + if (this.flutterNapi.isAttached()) { + this.flutterNapi.processPendingMessages(); + } + } + + getLifecycleChannel(): LifecycleChannel | null { + return this.lifecycleChannel; + } + + getNavigationChannel(): NavigationChannel | null { + return this.navigationChannel; + } + + getTextInputChannel(): TextInputChannel | null { + return this.textInputChannel; + } + + getPlatformChannel(): PlatformChannel | null { + return this.platformChannel; + } + + getSystemChannel(): SystemChannel | null { + return this.systemChannel; + } + + getLocaleChannel(): LocalizationChannel | null { + return this.localeChannel; + } + + getMouseCursorChannel(): MouseCursorChannel | null { + return this.mouseCursorChannel; + } + + getFlutterNapi(): FlutterNapi { + return this.flutterNapi; + } + + getFlutterRenderer(): FlutterRenderer { + return this.renderer; + } + + getDartExecutor(): DartExecutor { + return this.dartExecutor + } + + getPlugins(): PluginRegistry | null { + return this.pluginRegistry; + } + + getAbilityControlSurface(): AbilityControlSurface | null { + return this.pluginRegistry; + } + + getSettingsChannel() { + return this.settingsChannel; + } + + getFlutterLoader() { + return this.flutterLoader; + } + + onPreEngineRestart(): void { + this.engineLifecycleListeners.forEach(listener => listener.onPreEngineRestart()) + } + + onEngineWillDestroy(): void { + + } + + addEngineLifecycleListener(listener: EngineLifecycleListener): void { + this.engineLifecycleListeners.add(listener); + } + + removeEngineLifecycleListener(listener: EngineLifecycleListener): void { + this.engineLifecycleListeners.delete(listener); + } + + destroy(): void { + Log.d(TAG, "Destroying."); + this.engineLifecycleListeners.forEach(listener => listener.onEngineWillDestroy()) + this.flutterNapi.removeEngineLifecycleListener(this); + this.pluginRegistry?.detachFromAbility(); + this.platformViewsController?.onDetachedFromNapi(); + this.pluginRegistry?.destroy(); + this.dartExecutor.onDetachedFromNAPI(); + this.flutterNapi.detachFromNativeAndReleaseResources(); + } + + getRestorationChannel(): RestorationChannel | null { + return this.restorationChannel; + } + + getAccessibilityChannel(): AccessibilityChannel | null { + return this.accessibilityChannel; + } + + getLocalizationPlugin(): LocalizationPlugin | null { + return this.localizationPlugin; + } + + getSystemLanguages(): void { + return this.flutterNapi.getSystemLanguages(); + } + + getPlatformViewsController(): PlatformViewsController | null { + return this.platformViewsController; + } +} + +export interface EngineLifecycleListener { + onPreEngineRestart(): void; + + onEngineWillDestroy(): void; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineCache.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineCache.ets new file mode 100644 index 0000000000000000000000000000000000000000..9e65f2f748324b2827bd0103a33d0cad66d987a1 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineCache.ets @@ -0,0 +1,67 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import FlutterEngine from "./FlutterEngine" + +export default class FlutterEngineCache { + private static instance: FlutterEngineCache; + private cachedEngines: Map = new Map(); + + static getInstance(): FlutterEngineCache { + if (FlutterEngineCache.instance == null) { + FlutterEngineCache.instance = new FlutterEngineCache(); + } + return FlutterEngineCache.instance; + } + + /** + * 返回engineId对应的FlutterEngine是否存在 + */ + contains(engineId: String): boolean { + return this.cachedEngines.has(engineId); + } + + /** + * 返回engineId对应的FlutterEngine + */ + get(engineId: String): FlutterEngine | null { + return this.cachedEngines.get(engineId) || null; + } + + /** + * 将传入的FlutterEngine与engineId放在缓存中 + */ + put(engineId: String, engine: FlutterEngine | null): void { + if (engine != null) { + this.cachedEngines.set(engineId, engine); + } else { + this.cachedEngines.delete(engineId); + } + } + + /** + * 移除engineId对应的FlutterEngine + */ + remove(engineId: String): void { + this.put(engineId, null); + } + + /** + * 移除cachedEngines所有中所有的FlutterEngine + */ + clear(): void { + this.cachedEngines.clear(); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineConnectionRegistry.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineConnectionRegistry.ets new file mode 100644 index 0000000000000000000000000000000000000000..0e2d9f76b1df74c796f578c3cd73907817b15da6 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineConnectionRegistry.ets @@ -0,0 +1,284 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import PluginRegistry from './plugins/PluginRegistry'; +import { FlutterAssets, FlutterPlugin, FlutterPluginBinding } from './plugins/FlutterPlugin'; +import FlutterEngine from './FlutterEngine'; +import AbilityAware from './plugins/ability/AbilityAware'; +import UIAbility from '@ohos.app.ability.UIAbility'; +import { + AbilityPluginBinding, + WindowFocusChangedListener, + OnSaveStateListener, + NewWantListener +} from './plugins/ability/AbilityPluginBinding'; +import HashSet from '@ohos.util.HashSet'; +import Want from '@ohos.app.ability.Want'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import common from '@ohos.app.ability.common'; +import FlutterLoader from './loader/FlutterLoader'; +import Log from '../../util/Log'; +import ToolUtils from '../../util/ToolUtils'; +import AbilityControlSurface from './plugins/ability/AbilityControlSurface'; +import ExclusiveAppComponent from '../ohos/ExclusiveAppComponent'; +import FlutterEngineGroup from './FlutterEngineGroup'; +import Any from '../../plugin/common/Any'; + +const TAG = "FlutterEngineCxnRegistry"; + +export default class FlutterEngineConnectionRegistry implements PluginRegistry, AbilityControlSurface { + // PluginRegistry + private plugins = new Map(); + private defaultPlugin: FlutterPlugin = new EmptyPlugin(); + // Standard FlutterPlugin + private flutterEngine: FlutterEngine; + private pluginBinding: FlutterPluginBinding; + // AbilityAware + private abilityAwarePlugins = new Map(); + private exclusiveAbility: ExclusiveAppComponent | null = null; + private abilityPluginBinding: FlutterEngineAbilityPluginBinding | null = null; + + constructor(appContext: common.Context, flutterEngine: FlutterEngine, flutterLoader: FlutterLoader) { + this.flutterEngine = flutterEngine; + this.pluginBinding = new FlutterPluginBinding(appContext, flutterEngine, flutterEngine.getDartExecutor(), + new DefaultFlutterAssets(flutterLoader), flutterEngine.getFlutterRenderer(), + flutterEngine.getPlatformViewsController()?.getRegistry()); + } + + add(plugin: FlutterPlugin): void { + try { + if (this.has(plugin.getUniqueClassName())) { + Log.w( + TAG, + "Attempted to register plugin (" + + plugin + + ") but it was " + + "already registered with this FlutterEngine (" + + this.flutterEngine + + ")."); + return; + } + + Log.w(TAG, "Adding plugin: " + plugin.getUniqueClassName()); + // Add the plugin to our generic set of plugins and notify the plugin + // that is has been attached to an engine. + this.plugins.set(plugin.getUniqueClassName(), plugin); + plugin.onAttachedToEngine(this.pluginBinding); + + // For AbilityAware plugins, add the plugin to our set of AbilityAware + // plugins, and if this engine is currently attached to an Ability, + // notify the AbilityAware plugin that it is now attached to an Ability. + if (ToolUtils.implementsInterface(plugin, "onAttachedToAbility")) { + const abilityAware: Any = plugin; + this.abilityAwarePlugins.set(plugin.getUniqueClassName(), abilityAware); + if (this.isAttachedToAbility()) { + abilityAware.onAttachedToAbility(this.abilityPluginBinding); + } + } + } finally { + + } + } + + addList(plugins: Set): void { + plugins.forEach(plugin => this.add(plugin)) + } + + has(pluginClassName: string): boolean { + return this.plugins.has(pluginClassName); + } + + get(pluginClassName: string): FlutterPlugin { + return this.plugins.get(pluginClassName) ?? this.defaultPlugin; + } + + remove(pluginClassName: string): void { + const plugin = this.plugins.get(pluginClassName); + if (plugin == null) { + return; + } + if (ToolUtils.implementsInterface(plugin, "onAttachedToAbility")) { + if (this.isAttachedToAbility()) { + const abilityAware: Any = plugin; + abilityAware.onDetachedFromAbility(); + } + this.abilityAwarePlugins.delete(pluginClassName); + } + // Notify the plugin that is now detached from this engine. Then remove + // it from our set of generic plugins. + plugin.onDetachedFromEngine(this.pluginBinding); + this.plugins.delete(pluginClassName) + } + + removeList(pluginClassNames: Set): void { + pluginClassNames.forEach(plugin => this.remove(plugin)) + } + + removeAll(): void { + this.removeList(new Set(this.plugins.keys())); + this.plugins.clear(); + } + + private isAttachedToAbility(): boolean { + return this.exclusiveAbility != null; + } + + attachToAbility(exclusiveAbility: ExclusiveAppComponent): void { + if (this.exclusiveAbility != null) { + this.exclusiveAbility.detachFromFlutterEngine(); + } + // If we were already attached to an app component, detach from it. + this.detachFromAppComponent(); + this.exclusiveAbility = exclusiveAbility; + this.attachToAbilityInternal(exclusiveAbility.getAppComponent(),); + } + + detachFromAbility(): void { + if (this.isAttachedToAbility()) { + try { + this.abilityAwarePlugins.forEach(abilityAware => abilityAware.onDetachedFromAbility()) + } catch (e) { + Log.e(TAG, "abilityAwarePlugins DetachedFromAbility failed, msg:" + e); + } + this.detachFromAbilityInternal(); + } else { + Log.e(TAG, "Attempted to detach plugins from an Ability when no Ability was attached."); + } + } + + onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void { + this.abilityPluginBinding?.onNewWant(want, launchParams); + } + + onWindowFocusChanged(hasFocus: boolean): void { + this.abilityPluginBinding?.onWindowFocusChanged(hasFocus); + } + + onSaveState(reason: AbilityConstant.StateType, wantParam: Record): AbilityConstant.OnSaveResult { + return this.abilityPluginBinding?.onSaveState(reason, wantParam) ?? AbilityConstant.OnSaveResult.ALL_REJECT; + } + + private detachFromAppComponent(): void { + if (this.isAttachedToAbility()) { + this.detachFromAbility(); + } + } + + private attachToAbilityInternal(ability: UIAbility): void { + this.abilityPluginBinding = new FlutterEngineAbilityPluginBinding(ability); + // Notify all AbilityAware plugins that they are now attached to a new Ability. + this.abilityAwarePlugins.forEach(abilityAware => abilityAware.onAttachedToAbility(this.abilityPluginBinding!)); + } + + private detachFromAbilityInternal(): void { + this.exclusiveAbility = null; + this.abilityPluginBinding = null; + } + + destroy(): void { + this.detachFromAppComponent(); + // Remove all registered plugins. + this.removeAll(); + } +} + +class FlutterEngineAbilityPluginBinding implements AbilityPluginBinding { + private ability: UIAbility; + private onNewWantListeners = new HashSet(); + private onWindowFocusChangedListeners = new HashSet(); + private onSaveStateListeners = new HashSet(); + + constructor(ability: UIAbility) { + this.ability = ability; + + } + + getAbility(): UIAbility { + return this.ability; + } + + addOnNewWantListener(listener: NewWantListener): void { + this.onNewWantListeners.add(listener) + } + + removeOnNewWantListener(listener: NewWantListener): void { + this.onNewWantListeners.remove(listener) + } + + addOnWindowFocusChangedListener(listener: WindowFocusChangedListener): void { + this.onWindowFocusChangedListeners.add(listener) + } + + removeOnWindowFocusChangedListener(listener: WindowFocusChangedListener): void { + this.onWindowFocusChangedListeners.remove(listener) + } + + addOnSaveStateListener(listener: OnSaveStateListener) { + this.onSaveStateListeners.add(listener) + } + + removeOnSaveStateListener(listener: OnSaveStateListener) { + this.onSaveStateListeners.remove(listener) + } + + onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void { + this.onNewWantListeners.forEach((listener, key) => { + listener?.onNewWant(want, launchParams) + }); + } + + onWindowFocusChanged(hasFocus: boolean): void { + this.onWindowFocusChangedListeners.forEach((listener, key) => { + listener?.onWindowFocusChanged(hasFocus) + }); + } + + onSaveState(reason: AbilityConstant.StateType, wantParam: Record): AbilityConstant.OnSaveResult { + this.onSaveStateListeners.forEach((listener, key) => { + listener?.onSaveState(reason, wantParam) + }); + return AbilityConstant.OnSaveResult.ALL_AGREE; + } +} + +class DefaultFlutterAssets implements FlutterAssets { + private flutterLoader: FlutterLoader; + + constructor(flutterLoader: FlutterLoader) { + this.flutterLoader = flutterLoader; + } + + getAssetFilePathByName(assetFileName: string, packageName?: string): string { + return this.flutterLoader.getLookupKeyForAsset(assetFileName, packageName); + } + + getAssetFilePathBySubpath(assetSubpath: string, packageName?: string) { + return this.flutterLoader.getLookupKeyForAsset(assetSubpath, packageName); + } +} + +class EmptyPlugin implements FlutterPlugin { + getUniqueClassName(): string { + return ''; + } + + onAttachedToEngine(binding: FlutterPluginBinding) { + + } + + onDetachedFromEngine(binding: FlutterPluginBinding) { + + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineGroup.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineGroup.ets new file mode 100644 index 0000000000000000000000000000000000000000..c763423a3a6c64c479dbc7983dd1aef73f94c2b5 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineGroup.ets @@ -0,0 +1,188 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import FlutterEngine, { EngineLifecycleListener } from "./FlutterEngine" +import common from '@ohos.app.ability.common' +import display from '@ohos.display'; +import FlutterLoader from './loader/FlutterLoader' +import FlutterInjector from '../../FlutterInjector' +import { DartEntrypoint } from './dart/DartExecutor' +import PlatformViewsController from '../../plugin/platform/PlatformViewsController' +import ArrayList from '@ohos.util.ArrayList' +import Log from '../../util/Log'; +import FlutterManager from '../ohos/FlutterManager'; + +const TAG = "FlutterEngineGroup" + +export default class FlutterEngineGroup { + private activeEngines: ArrayList = new ArrayList(); + + constructor() { + + } + + + checkLoader(context: common.Context, args: Array) { + let loader: FlutterLoader = FlutterInjector.getInstance().getFlutterLoader(); + if (!loader.initialized) { + loader.startInitialization(context); + loader.ensureInitializationComplete(args); + } + } + + createAndRunEngineByOptions(options: Options) { + let engine: FlutterEngine | null = null; + let context: common.Context = options.getContext(); + let dartEntrypoint: DartEntrypoint | null = options.getDartEntrypoint(); + let initialRoute: string = options.getInitialRoute(); + let dartEntrypointArgs: Array = options.getDartEntrypointArgs(); + let platformViewsController: PlatformViewsController | null = options.getPlatformViewsController(); + let waitForRestorationData: boolean = options.getWaitForRestorationData(); + + if (dartEntrypoint == null) { + dartEntrypoint = DartEntrypoint.createDefault(); + } + + if (platformViewsController == null) { + platformViewsController = new PlatformViewsController(); + } + + Log.i(TAG, "shellHolder, this.activeEngines.length=" + this.activeEngines.length) + if (this.activeEngines.length == 0) { + engine = this.createEngine(context, platformViewsController); + engine.init(context, null, // String[]. The Dart VM has already started, this arguments will have no effect. + waitForRestorationData) + if (initialRoute != null) { + engine.getNavigationChannel()?.setInitialRoute(initialRoute); + } + engine.getDartExecutor().executeDartEntrypoint(dartEntrypoint, dartEntrypointArgs) + } else { + engine = this.activeEngines[0] + .spawn( + context, + dartEntrypoint, + initialRoute, + dartEntrypointArgs, + platformViewsController, + waitForRestorationData); + } + this.activeEngines.add(engine); + + const engineToCleanUpOnDestroy = engine; + let listener: EngineLifecycleListener = new EngineLifecycleListenerImpl( + platformViewsController, + this.activeEngines, + engineToCleanUpOnDestroy); + engine?.addEngineLifecycleListener(listener); + return engine; + } + + createEngine(context: common.Context, platformViewsController: PlatformViewsController): FlutterEngine { + return new FlutterEngine(context, null, null, platformViewsController); + } + + getDefaultEngine(): FlutterEngine | null { + let engine: FlutterEngine | null = null; + if (this.activeEngines.length != 0) { + engine = this.activeEngines[0]; + } + return engine; + } +} + +class EngineLifecycleListenerImpl implements EngineLifecycleListener { + private platformViewsController: PlatformViewsController; + private activeEngines: ArrayList = new ArrayList(); + private engine: FlutterEngine | null; + + constructor( + platformViewsController: PlatformViewsController, + activeEngines: ArrayList, + engine: FlutterEngine | null) { + this.platformViewsController = platformViewsController; + this.activeEngines = activeEngines; + this.engine = engine; + } + + onPreEngineRestart(): void { + this.platformViewsController.onPreEngineRestart(); + } + + onEngineWillDestroy(): void { + this.activeEngines.remove(this.engine); + } +} + +export class Options { + private context: common.Context; + private dartEntrypoint: DartEntrypoint | null = null; + private initialRoute: string = ''; + private dartEntrypointArgs: Array = []; + private platformViewsController: PlatformViewsController | null = null; + private waitForRestorationData: boolean = false; + + constructor(context: common.Context) { + this.context = context; + } + + getContext(): common.Context { + return this.context; + } + + getDartEntrypoint(): DartEntrypoint | null { + return this.dartEntrypoint; + } + + getInitialRoute(): string { + return this.initialRoute; + } + + getDartEntrypointArgs(): Array { + return this.dartEntrypointArgs; + } + + getWaitForRestorationData(): boolean { + return this.waitForRestorationData; + } + + getPlatformViewsController(): PlatformViewsController | null { + return this.platformViewsController; + } + + setDartEntrypoint(dartEntrypoint: DartEntrypoint): Options { + this.dartEntrypoint = dartEntrypoint; + return this; + } + + setInitialRoute(initialRoute: string): Options { + this.initialRoute = initialRoute; + return this; + } + + setDartEntrypointArgs(dartEntrypointArgs: Array): Options { + this.dartEntrypointArgs = dartEntrypointArgs; + return this; + } + + setWaitForRestorationData(waitForRestorationData: boolean): Options { + this.waitForRestorationData = waitForRestorationData; + return this; + } + + setPlatformViewsController(platformViewsController: PlatformViewsController): Options { + this.platformViewsController = platformViewsController; + return this; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineGroupCache.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineGroupCache.ets new file mode 100644 index 0000000000000000000000000000000000000000..c4494959a496c43fb9eba2822fd633c3c303cd13 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineGroupCache.ets @@ -0,0 +1,41 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import FlutterEngineGroup from './FlutterEngineGroup'; + +export default class FlutterEngineGroupCache { + static readonly instance = new FlutterEngineGroupCache(); + private cachedEngineGroups = new Map(); + + contains(engineGroupId: string): boolean { + return this.cachedEngineGroups.has(engineGroupId); + } + + get(engineGroupId: string): FlutterEngineGroup | null { + return this.cachedEngineGroups.get(engineGroupId) ?? null; + } + + put(engineGroupId: string, engineGroup?: FlutterEngineGroup) { + if (engineGroup != null) { + this.cachedEngineGroups.set(engineGroupId, engineGroup); + } else { + this.cachedEngineGroups.delete(engineGroupId); + } + } + + clear(): void { + this.cachedEngineGroups.clear(); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEnginePreload.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEnginePreload.ets new file mode 100644 index 0000000000000000000000000000000000000000..807d32354be84d9d4ae652f56dafcebd03be664b --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEnginePreload.ets @@ -0,0 +1,198 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import FlutterEngine, { EngineLifecycleListener } from "./FlutterEngine" +import common from '@ohos.app.ability.common' +import display from '@ohos.display'; +import FlutterLoader from './loader/FlutterLoader' +import Log from '../../util/Log'; +import FlutterManager from '../ohos/FlutterManager'; +import FlutterInjector from '../../FlutterInjector' +import FlutterEngineCache from './FlutterEngineCache'; +import FlutterEngineGroupCache from './FlutterEngineGroupCache'; +import FlutterAbilityLaunchConfigs from '../ohos/FlutterAbilityLaunchConfigs'; +import JSONMethodCodec from '../../plugin/common/JSONMethodCodec'; +import MethodCall from '../../plugin/common/MethodCall'; +import FlutterNapi from './FlutterNapi'; +import { ViewportMetrics } from '../../view/FlutterView'; + +const TAG = "FlutterEnginePreload" + +export default class FlutterEnginePreload { + static async preloadEngine(context: common.Context, params: Record = {}, + nextViewId: string | null = null) { + let loader: FlutterLoader = FlutterInjector.getInstance().getFlutterLoader(); + let dartArgs = new Array(); + if (params[FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT_ARGS]) { + dartArgs = params[FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT_ARGS] as Array; + } + + if (!loader.initialized) { + loader.startInitialization(context); + loader.ensureInitializationComplete(dartArgs); + } + + let flutterNapi: FlutterNapi | null = + FlutterEnginePreload.preLoadFlutterNapi(context, loader.findAppBundlePath(), params) + let viewportMetrics: ViewportMetrics = new ViewportMetrics(); + if (params[FlutterAbilityLaunchConfigs.PRELOAD_VIEWPORT_METRICS_KEY]) { + viewportMetrics = params[FlutterAbilityLaunchConfigs.PRELOAD_VIEWPORT_METRICS_KEY] as ViewportMetrics; + } else { + let display_info: display.Display = display.getDefaultDisplaySync(); + viewportMetrics.physicalWidth = display_info.width; + viewportMetrics.physicalHeight = display_info.height; + viewportMetrics.devicePixelRatio = display_info.densityPixels; + viewportMetrics.physicalTouchSlop = 1.0 * display_info.densityPixels; + } + if (flutterNapi) { + if (!nextViewId) { + nextViewId = FlutterManager.getInstance().getNextFlutterViewId(); + } + flutterNapi.setPreloading(); + flutterNapi.xComponentPreDraw(nextViewId, viewportMetrics.physicalWidth, viewportMetrics.physicalHeight); + flutterNapi.setViewportMetrics(viewportMetrics.devicePixelRatio, + viewportMetrics.physicalWidth, + viewportMetrics.physicalHeight, + viewportMetrics.physicalViewPaddingTop, + viewportMetrics.physicalViewPaddingRight, + viewportMetrics.physicalViewPaddingBottom, + viewportMetrics.physicalViewPaddingLeft, + viewportMetrics.physicalViewInsetTop, + viewportMetrics.physicalViewInsetRight, + viewportMetrics.physicalViewInsetBottom, + viewportMetrics.physicalViewInsetLeft, + viewportMetrics.systemGestureInsetTop, + viewportMetrics.systemGestureInsetRight, + viewportMetrics.systemGestureInsetBottom, + viewportMetrics.systemGestureInsetLeft, + viewportMetrics.physicalTouchSlop, + new Array(0), + new Array(0), + new Array(0) + ); + } + } + + static predrawEngine(engine: FlutterEngine, params: Record = {}, nextViewId: string | null = null) { + if (!engine) { + return; + } + let flutterNapi = engine.getFlutterNapi(); + if (!flutterNapi.isAttached()) { + flutterNapi.attachToNative(); + } + if (!nextViewId) { + nextViewId = FlutterManager.getInstance().getNextFlutterViewId(); + } + let viewportMetrics: ViewportMetrics = new ViewportMetrics(); + if (params[FlutterAbilityLaunchConfigs.PRELOAD_VIEWPORT_METRICS_KEY]) { + viewportMetrics = params[FlutterAbilityLaunchConfigs.PRELOAD_VIEWPORT_METRICS_KEY] as ViewportMetrics; + } else { + let display_info: display.Display = display.getDefaultDisplaySync(); + viewportMetrics.physicalWidth = display_info.width; + viewportMetrics.physicalHeight = display_info.height; + viewportMetrics.devicePixelRatio = display_info.densityPixels; + viewportMetrics.physicalTouchSlop = 1.0 * display_info.densityPixels; + } + flutterNapi.setPreloading(); + flutterNapi.xComponentPreDraw(nextViewId, viewportMetrics.physicalWidth, viewportMetrics.physicalHeight); + flutterNapi.setViewportMetrics(viewportMetrics.devicePixelRatio, + viewportMetrics.physicalWidth, + viewportMetrics.physicalHeight, + viewportMetrics.physicalViewPaddingTop, + viewportMetrics.physicalViewPaddingRight, + viewportMetrics.physicalViewPaddingBottom, + viewportMetrics.physicalViewPaddingLeft, + viewportMetrics.physicalViewInsetTop, + viewportMetrics.physicalViewInsetRight, + viewportMetrics.physicalViewInsetBottom, + viewportMetrics.physicalViewInsetLeft, + viewportMetrics.systemGestureInsetTop, + viewportMetrics.systemGestureInsetRight, + viewportMetrics.systemGestureInsetBottom, + viewportMetrics.systemGestureInsetLeft, + viewportMetrics.physicalTouchSlop, + new Array(0), + new Array(0), + new Array(0) + ); + } + + static preLoadFlutterNapi(context: common.Context, bundlePath: string, + params: Record = {}): FlutterNapi | null { + + let cachedEngineId = params[FlutterAbilityLaunchConfigs.EXTRA_CACHED_ENGINE_ID] as string; + let cachedEngineGroupId = params[FlutterAbilityLaunchConfigs.EXTRA_CACHED_ENGINE_GROUP_ID] as string; + + let dartEntrypoint = FlutterAbilityLaunchConfigs.DEFAULT_DART_ENTRYPOINT; + if (params[FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT]) { + dartEntrypoint = params[FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT] as string; + } + + let dartEntrypointLibraryUri = ""; + if (params[FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT_LIBRARY_URI]) { + dartEntrypointLibraryUri = params[FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT_LIBRARY_URI] as string; + } + + let initialRoute = ""; + if (params[FlutterAbilityLaunchConfigs.EXTRA_INITIAL_ROUTE]) { + initialRoute = params[FlutterAbilityLaunchConfigs.EXTRA_INITIAL_ROUTE] as string + } + + let dartArgs = new Array(); + if (params[FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT_ARGS]) { + dartArgs = params[FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT_ARGS] as Array; + } + + Log.d(TAG, "cachedEngineId=" + cachedEngineId); + let flutterNapi: FlutterNapi | null = null; + if (cachedEngineId && cachedEngineId.length > 0) { + let engine = FlutterEngineCache.getInstance().get(cachedEngineId); + if (engine) { + flutterNapi = engine.getFlutterNapi(); + } + } + if (cachedEngineGroupId && cachedEngineGroupId.length > 0) { + let flutterEngineGroup = FlutterEngineGroupCache.instance.get(cachedEngineGroupId); + if (flutterEngineGroup) { + let defaultEngine = flutterEngineGroup.getDefaultEngine(); + if (defaultEngine) { + let oldFlutterNapi = defaultEngine.getFlutterNapi(); + return oldFlutterNapi.preSpawn(dartEntrypoint, dartEntrypointLibraryUri, initialRoute, dartArgs); + } + } + } + + if (!flutterNapi) { + flutterNapi = FlutterInjector.getInstance().getPreloadFlutterNapi(); + } + if (flutterNapi) { + if (!flutterNapi.isAttached()) { + flutterNapi.attachToNative(); + } + Log.d(TAG, "setInitialRoute: " + initialRoute); + let message = JSONMethodCodec.INSTANCE.encodeMethodCall(new MethodCall("setInitialRoute", initialRoute)); + flutterNapi.dispatchPlatformMessage("flutter/navigation", message, message.byteLength, 0); + + flutterNapi.runBundleAndSnapshotFromLibrary( + bundlePath, + dartEntrypoint, + dartEntrypointLibraryUri, + context.resourceManager, + dartArgs); + } + return flutterNapi; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterNapi.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterNapi.ets new file mode 100644 index 0000000000000000000000000000000000000000..d1cd08346821075c39c0e7f2b86fd96558b5d4f1 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterNapi.ets @@ -0,0 +1,730 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import flutter from 'libflutter.so'; +import common from '@ohos.app.ability.common'; +import Log from '../../util/Log'; +import resourceManager from '@ohos.resourceManager'; +import { PlatformMessageHandler } from './dart/PlatformMessageHandler'; +import { FlutterCallbackInformation } from '../../view/FlutterCallbackInformation'; +import image from '@ohos.multimedia.image'; +import { EngineLifecycleListener } from './FlutterEngine'; +import { ByteBuffer } from '../../util/ByteBuffer'; +import LocalizationPlugin from '../../plugin/localization/LocalizationPlugin'; +import i18n from '@ohos.i18n'; +import Any from '../../plugin/common/Any'; +import FlutterManager from '../ohos/FlutterManager'; +import deviceInfo from '@ohos.deviceInfo'; +import TouchEventProcessor from '../ohos/TouchEventProcessor'; +import { EmbeddingNodeController } from '../ohos/EmbeddingNodeController'; +import BuildProfile from '../../../../../BuildProfile'; +import { Action } from '../engine/systemchannels/AccessibilityChannel'; + +const TAG = "FlutterNapi"; + +enum ContextType { + APP_LIFECYCLE = 0, + JS_PAGE_LIFECYCLE, +} + +interface PendingMessage { + channel: string; + message: ArrayBuffer; + replyId: number; + messageData: number; +} + +/** + * 提供arkTs的flutterNAPI接口 + */ +export default class FlutterNapi { + private static hasInit: boolean = false; + //是否已实现 + hasImplemented: boolean = false; + nativeShellHolderId: number | null = null; + platformMessageHandler: PlatformMessageHandler | null = null; + private engineLifecycleListeners = new Set(); + accessibilityDelegate: AccessibilityDelegate | null = null; + localizationPlugin: LocalizationPlugin | null = null; + isDisplayingFlutterUi: boolean = false; + isPreloadedFlutterUi: boolean = false; + isRunningDart: boolean = false; + private nextSpawnNapi: FlutterNapi | null = null; + private pendingMessages: PendingMessage[] = []; + private readyForHandleMessage: boolean = true; + private firstPreloading: boolean = true; + + /** + * 更新刷新率 + * @param rate + */ + updateRefreshRate(refreshRateFPS: number) { + flutter.nativeUpdateRefreshRate(refreshRateFPS); + } + + updateSize(width: number, height: number) { + flutter.nativeUpdateSize(width, height); + } + + updateDensity(densityPixels: number) { + flutter.nativeUpdateDensity(densityPixels); + } + + init(context: common.Context, + args: Array, + bundlePath: string, + appStoragePath: string, + engineCachesPath: string, + initTimeMillis: number) { + if (FlutterNapi.hasInit) { + Log.e(TAG, "the engine has init"); + return; + } + Log.d(TAG, "HAR_VERSION=" + BuildProfile.HAR_VERSION); + Log.d(TAG, JSON.stringify({ + "name": "init, initTimeMillis=" + initTimeMillis, + "bundlePath": bundlePath, + "appStoragePath": appStoragePath, + "engineCachesPath": engineCachesPath, + "args": args, + })); + let code: number | null = flutter.nativeInit(context, args, bundlePath, appStoragePath, + engineCachesPath, initTimeMillis, deviceInfo.productModel); + FlutterNapi.hasInit = code == 0; + Log.d(TAG, "init code=" + code + ", FlutterNapi.hasInit" + FlutterNapi.hasInit); + } + + static prefetchDefaultFontManager(): void { + flutter.nativePrefetchDefaultFontManager(); + } + + checkAndReloadFont(): void { + flutter.nativeCheckAndReloadFont(this.nativeShellHolderId!); + } + + attachToNative(): void { + if (!FlutterNapi.hasInit) { + Log.e(TAG, "attachToNative fail, FlutterNapi.hasInit=" + FlutterNapi.hasInit); + return; + } + if (this.nativeShellHolderId == null) { + this.nativeShellHolderId = flutter.nativeAttach(this); + } + Log.d(TAG, "nativeShellHolderId=" + this.nativeShellHolderId); + } + + runBundleAndSnapshotFromLibrary( + bundlePath: string, + entrypointFunctionName: string | undefined, + pathToEntrypointFunction: string | undefined, + assetManager: resourceManager.ResourceManager, + entrypointArgs: Array) { + if (!FlutterNapi.hasInit) { + Log.e(TAG, "runBundleAndSnapshotFromLibrary fail, FlutterNapi.hasInit=" + FlutterNapi.hasInit); + return; + } + Log.d(TAG, "init: bundlePath=" + bundlePath + " entrypointFunctionName=" + entrypointFunctionName + + " pathToEntrypointFunction=" + pathToEntrypointFunction + " entrypointArgs=" + JSON.stringify(entrypointArgs)) + if (!this.nativeShellHolderId) { + Log.e(TAG, "runBundleAndSnapshotFromLibrary this.nativeShellHolderId = " + this.nativeShellHolderId) + return; + } + flutter.nativeRunBundleAndSnapshotFromLibrary(this.nativeShellHolderId!, bundlePath, entrypointFunctionName, + pathToEntrypointFunction, assetManager, entrypointArgs); + this.isRunningDart = true; + this.isDisplayingFlutterUi = false; + this.isPreloadedFlutterUi = false; + }; + + /** + * 当前so方法是否都实现 + * @returns + */ + checkImplemented(methodName: string = ""): boolean { + if (!this.hasImplemented) { + Log.e(TAG, "this method has not implemented -> " + methodName) + } + return this.hasImplemented; + } + + setPlatformMessageHandler(platformMessageHandler: PlatformMessageHandler | null): void { + this.ensureRunningOnMainThread(); + this.platformMessageHandler = platformMessageHandler; + } + + private nativeNotifyLowMemoryWarning(nativeShellHolderId: number): void { + + } + + static nativeLookupCallbackInformation(handle: number): FlutterCallbackInformation | null { + let callbackInformation = new FlutterCallbackInformation(); + let ret: number = flutter.nativeLookupCallbackInformation(callbackInformation, handle); + if (ret == 0) { + return callbackInformation; + } + return null; + } + + notifyLowMemoryWarning(): void { + this.ensureRunningOnMainThread(); + if (!this.nativeShellHolderId) { + Log.e(TAG, "notifyLowMemoryWarning this.nativeShellHolderId = " + this.nativeShellHolderId) + return; + } + this.nativeNotifyLowMemoryWarning(this.nativeShellHolderId!); + } + + isAttached(): boolean { + return this.nativeShellHolderId != null; + } + + private ensureRunningOnMainThread(): void { + + } + + dispatchEmptyPlatformMessage(channel: String, responseId: number): void { + this.ensureRunningOnMainThread(); + if (this.isAttached()) { + if (!this.nativeShellHolderId) { + Log.e(TAG, "dispatchEmptyPlatformMessage this.nativeShellHolderId = " + this.nativeShellHolderId) + return; + } + flutter.nativeDispatchEmptyPlatformMessage(this.nativeShellHolderId!, channel, responseId); + } else { + Log.w( + TAG, + "Tried to send a platform message to Flutter, but FlutterNapi was detached from native C++. Could not send. Channel: " + + channel + + ". Response ID: " + + responseId); + } + } + + /** Sends a reply {@code message} from HarmonyOS to Flutter over the given {@code channel}. */ + dispatchPlatformMessage(channel: String, message: ArrayBuffer, position: number, responseId: number): void { + this.ensureRunningOnMainThread(); + if (this.isAttached()) { + if (!this.nativeShellHolderId) { + Log.e(TAG, "dispatchPlatformMessage this.nativeShellHolderId = " + this.nativeShellHolderId) + return; + } + flutter.nativeDispatchPlatformMessage(this.nativeShellHolderId!, channel, message, position, responseId); + } else { + Log.w( + TAG, + "Tried to send a platform message to Flutter, but FlutterNapi was detached from native C++. Could not send. Channel: " + + channel + + ". Response ID: " + + responseId); + } + } + + invokePlatformMessageEmptyResponseCallback(responseId: number): void { + if (this.isAttached()) { + if (!this.nativeShellHolderId) { + Log.e(TAG, "invokePlatformMessageEmptyResponseCallback this.nativeShellHolderId = " + this.nativeShellHolderId) + return; + } + flutter.nativeInvokePlatformMessageEmptyResponseCallback(this.nativeShellHolderId!, responseId); + } else { + Log.w( + TAG, + "Tried to send a platform message response, but FlutterNapi was detached from native C++. Could not send. Response ID: " + + responseId); + } + } + + invokePlatformMessageResponseCallback(responseId: number, message: ArrayBuffer, position: number) { + if (this.isAttached()) { + if (!this.nativeShellHolderId) { + Log.e(TAG, "this.nativeShellHolderId = " + this.nativeShellHolderId) + return; + } + flutter.nativeInvokePlatformMessageResponseCallback( + this.nativeShellHolderId!, responseId, message, position); + } else { + Log.w( + TAG, + "Tried to send a platform message response, but FlutterNapi was detached from native C++. Could not send. Response ID: " + + responseId); + } + } + + setViewportMetrics(devicePixelRatio: number, physicalWidth: number + , physicalHeight: number, physicalPaddingTop: number, physicalPaddingRight: number + , physicalPaddingBottom: number, physicalPaddingLeft: number, physicalViewInsetTop: number + , physicalViewInsetRight: number, physicalViewInsetBottom: number, physicalViewInsetLeft: number + , systemGestureInsetTop: number, systemGestureInsetRight: number, systemGestureInsetBottom: number + , systemGestureInsetLeft: number, physicalTouchSlop: number, displayFeaturesBounds: Array + , displayFeaturesType: Array, displayFeaturesState: Array): void { + if (this.isAttached()) { + if (!this.nativeShellHolderId) { + Log.e(TAG, "setViewportMetrics this.nativeShellHolderId = " + this.nativeShellHolderId) + return; + } + flutter.nativeSetViewportMetrics(this.nativeShellHolderId!, devicePixelRatio, + physicalWidth, + physicalHeight, + physicalPaddingTop, + physicalPaddingRight, + physicalPaddingBottom, + physicalPaddingLeft, + physicalViewInsetTop, + physicalViewInsetRight, + physicalViewInsetBottom, + physicalViewInsetLeft, + systemGestureInsetTop, + systemGestureInsetRight, + systemGestureInsetBottom, + systemGestureInsetLeft, + physicalTouchSlop, + displayFeaturesBounds, + displayFeaturesType, + displayFeaturesState); + } + } + + spawn(entrypointFunctionName: string, pathToEntrypointFunction: string, initialRoute: string, + entrypointArgs: Array): FlutterNapi { + if (this.nextSpawnNapi) { + let ret = this.nextSpawnNapi; + this.nextSpawnNapi = null; + return ret; + } + let flutterNapi = new FlutterNapi(); + let shellHolderId: number = + flutter.nativeSpawn(this.nativeShellHolderId, entrypointFunctionName, pathToEntrypointFunction, initialRoute, + entrypointArgs, flutterNapi); + flutterNapi.nativeShellHolderId = shellHolderId; + flutterNapi.isRunningDart = this.isRunningDart; + flutterNapi.isDisplayingFlutterUi = false; + flutterNapi.isPreloadedFlutterUi = false; + return flutterNapi; + } + + preSpawn(entrypointFunctionName: string, pathToEntrypointFunction: string, initialRoute: string, + entrypointArgs: Array): FlutterNapi { + if (this.nextSpawnNapi) { + this.nextSpawnNapi.detachFromNativeAndReleaseResources(); + } + let flutterNapi = new FlutterNapi(); + let shellHolderId: number = + flutter.nativeSpawn(this.nativeShellHolderId, entrypointFunctionName, pathToEntrypointFunction, initialRoute, + entrypointArgs, flutterNapi); + flutterNapi.nativeShellHolderId = shellHolderId; + flutterNapi.isRunningDart = this.isRunningDart; + flutterNapi.isDisplayingFlutterUi = false; + flutterNapi.isPreloadedFlutterUi = false; + this.nextSpawnNapi = flutterNapi; + return flutterNapi; + } + + addEngineLifecycleListener(engineLifecycleListener: EngineLifecycleListener): void { + this.engineLifecycleListeners.add(engineLifecycleListener); + } + + removeEngineLifecycleListener(engineLifecycleListener: EngineLifecycleListener) { + this.engineLifecycleListeners.delete(engineLifecycleListener); + } + + //Called by native to respond to a platform message that we sent. + handlePlatformMessageResponse(replyId: number, reply: ArrayBuffer): void { + Log.d(TAG, "called handlePlatformMessageResponse Response ID: " + replyId); + if (this.platformMessageHandler != null) { + this.platformMessageHandler.handlePlatformMessageResponse(replyId, reply); + } + } + + // Called by native on any thread. + handlePlatformMessage(channel: string, message: ArrayBuffer, replyId: number, messageData: number): void { + Log.d(TAG, "called handlePlatformMessage Channel: " + channel + ". Response ID: " + replyId); + if (this.platformMessageHandler != null && this.readyForHandleMessage) { + this.platformMessageHandler.handleMessageFromDart(channel, message, replyId, messageData); + } else { + const pendingMessage: PendingMessage = { + channel, + message, + replyId, + messageData + }; + this.pendingMessages.push(pendingMessage); + } + } + + setPreloading(): void { + if (this.firstPreloading) { + this.readyForHandleMessage = false; + this.firstPreloading = false; + } + } + + processPendingMessages(): void { + Log.d(TAG, "processPendingMessages len:" + this.pendingMessages.length); + this.readyForHandleMessage = true; + while (this.pendingMessages.length > 0 && this.platformMessageHandler) { + const pendingMessage = this.pendingMessages.shift(); + if (pendingMessage) { + this.platformMessageHandler.handleMessageFromDart( + pendingMessage.channel, + pendingMessage.message, + pendingMessage.replyId, + pendingMessage.messageData + ); + } + } + } + + // Called by native to notify first Flutter frame rendered. + onFirstFrame(isPreload: number): void { + Log.d(TAG, "called onFirstFrame isPreload:" + isPreload); + if (isPreload) { + this.isPreloadedFlutterUi = true; + } else { + this.processPendingMessages(); + if (this.isDisplayingFlutterUi) { + return; + } + this.isDisplayingFlutterUi = true; + } + FlutterManager.getInstance().getFlutterViewList().forEach((value) => { + if (this.nativeShellHolderId != null && value.isSameEngineShellHolderId(this.nativeShellHolderId)) { + value.onFirstFrame(isPreload); + } + }); + } + + // Called by native. + onPreEngineRestart(): void { + Log.d(TAG, "called onPreEngineRestart") + this.engineLifecycleListeners.forEach(listener => listener.onPreEngineRestart()); + } + + // /** Invoked by native to obtain the results of OHOS's locale resolution algorithm. */ + computePlatformResolvedLocale(strings: Array): Array { + Log.d(TAG, "called computePlatformResolvedLocale " + JSON.stringify(strings)) + return [] + } + + setSemanticsEnabledWithRespId(enabled: boolean, responseId: number): void { + this.ensureRunningOnMainThread(); + if (this.isAttached()) { + flutter.nativeSetSemanticsEnabled(this.nativeShellHolderId!, enabled); + } else { + Log.w( + TAG, + "Tried to send a platform message response, but FlutterNapi was detached from native C++. Could not send. Response ID: " + + responseId); + } + } + + setSemanticsEnabled(enabled: boolean): void { + this.ensureRunningOnMainThread(); + if (this.isAttached()) { + flutter.nativeSetSemanticsEnabled(this.nativeShellHolderId!, enabled); + } else { + Log.e( + TAG, + "Tried to send a platform message response, but FlutterNapi was detached from native C++. Could not send."); + } + } + + setAccessibilityFeatures(accessibilityFeatureFlags: number, responseId: number): void { + if (this.isAttached()) { + flutter.nativeSetAccessibilityFeatures(accessibilityFeatureFlags, responseId); + } else { + Log.w( + TAG, + "Tried to send a platform message response, but FlutterNapi was detached from native C++. Could not send. Response ID: " + + responseId); + } + } + + nativeSetAccessibilityFeatures(accessibilityFeatureFlags: number, responseId: number): void { + } + + dispatchSemanticsAction(virtualViewId: number, action: Action, responseId: number): void { + if (this.isAttached()) { + this.nativeDispatchSemanticsAction(virtualViewId, action, responseId); + } else { + Log.w( + TAG, + "Tried to send a platform message response, but FlutterNapi was detached from native C++. Could not send. Response ID: " + + responseId); + } + } + + nativeDispatchSemanticsAction(virtualViewId: number, action: Action, responseId: number): void { + } + + setAccessibilityDelegate(delegate: AccessibilityDelegate, responseId: number): void { + if (this.isAttached()) { + this.accessibilityDelegate = delegate; + } else { + Log.w( + TAG, + "Tried to send a platform message response, but FlutterNapi was detached from native C++. Could not send. Response ID: " + + responseId); + } + } + + accessibilityStateChange(state: Boolean): void { + this.ensureRunningOnMainThread(); + if (this.accessibilityDelegate != null) { + this.accessibilityDelegate.accessibilityStateChange(state); + } + Log.d(TAG, "accessibilityStateChange is called"); + flutter.nativeAccessibilityStateChange(this.nativeShellHolderId!, state); + } + + setLocalizationPlugin(localizationPlugin: LocalizationPlugin | null): void { + this.localizationPlugin = localizationPlugin; + } + + /** + * 获取系统语言列表 + * @param rate + */ + getSystemLanguages() { + Log.d(TAG, "called getSystemLanguages ") + let index: number; + let systemLanguages = i18n.System.getPreferredLanguageList(); + for (index = 0; index < systemLanguages.length; index++) { + Log.d(TAG, "systemlanguages " + index + ":" + systemLanguages[index]); + } + if (!this.nativeShellHolderId) { + Log.e(TAG, "getSystemLanguages this.nativeShellHolderId = " + this.nativeShellHolderId) + return; + } + flutter.nativeGetSystemLanguages(this.nativeShellHolderId!, systemLanguages); + } + + /** + * xcomponet绑定flutterEngine + * @param xcomponentId + */ + xComponentAttachFlutterEngine(xcomponentId: string) { + flutter.nativeXComponentAttachFlutterEngine(xcomponentId, this.nativeShellHolderId!); + } + + /** + * xcomponet预渲染 + * @param xcomponentId + */ + xComponentPreDraw(xcomponentId: string, width: number, height: number) { + flutter.nativeXComponentPreDraw(xcomponentId, this.nativeShellHolderId!, width, height); + } + + /** + * xcomponet解除绑定flutterEngine + * @param xcomponentId + */ + xComponentDetachFlutterEngine(xcomponentId: string) { + flutter.nativeXComponentDetachFlutterEngine(xcomponentId, this.nativeShellHolderId!); + } + + /** + * xcomponent send mouseWheel event to flutterEngine + * @param xcomponentId + * @param eventType + * @param event + */ + xComponentDisPatchMouseWheel(xcomponentId: string, eventType: string, event: PanGestureEvent) { + // only mouse + if (event.source !== SourceType.Mouse) { + return; + } + const vaildFinger = event.fingerList?.find(item => item.globalX && item.globalY); + if (!vaildFinger) { + return; + } + flutter.nativeXComponentDispatchMouseWheel( + this.nativeShellHolderId!!, + xcomponentId, + eventType, + vaildFinger?.id, + vaildFinger?.localX, + vaildFinger?.localY, + event.offsetY, + event.timestamp + ); + } + + detachFromNativeAndReleaseResources() { + if (!this.nativeShellHolderId) { + Log.e(TAG, "detachFromNativeAndReleaseResources this.nativeShellHolderId = " + this.nativeShellHolderId) + return; + } + flutter.nativeDestroy(this.nativeShellHolderId!!); + this.nativeShellHolderId = null; + this.isRunningDart = false; + this.isDisplayingFlutterUi = false; + this.isPreloadedFlutterUi = false; + this.readyForHandleMessage = false; + } + + unregisterTexture(textureId: number): void { + Log.d(TAG, "called unregisterTexture "); + if (!this.nativeShellHolderId) { + Log.e(TAG, "unregisterTexture this.nativeShellHolderId = " + this.nativeShellHolderId) + return; + } + flutter.nativeUnregisterTexture(this.nativeShellHolderId!, textureId); + } + + registerPixelMap(textureId: number, pixelMap: PixelMap): void { + Log.d(TAG, "called registerPixelMap "); + if (!this.nativeShellHolderId) { + Log.e(TAG, "registerPixelMap this.nativeShellHolderId = " + this.nativeShellHolderId) + return; + } + flutter.nativeRegisterPixelMap(this.nativeShellHolderId!, textureId, pixelMap); + } + + setTextureBackGroundPixelMap(textureId: number, pixelMap: PixelMap): void { + if (this.isAttached()) { + if (!this.nativeShellHolderId) { + Log.e(TAG, "this.nativeShellHolderId = " + this.nativeShellHolderId) + return; + } + flutter.nativeSetTextureBackGroundPixelMap(this.nativeShellHolderId!, textureId, pixelMap); + } else { + return; + } + } + + registerTexture(textureId: number): number { + Log.d(TAG, "called registerTexture "); + if (!this.nativeShellHolderId) { + Log.e(TAG, "registerTexture this.nativeShellHolderId = " + this.nativeShellHolderId) + return 0; + } + return flutter.nativeRegisterTexture(this.nativeShellHolderId!, textureId); + } + + getTextureNativeWindowId(textureId: number): number { + Log.d(TAG, "called getTextureNativeWindowId "); + if (this.isAttached()) { + if (!this.nativeShellHolderId) { + Log.e(TAG, "this.nativeShellHolderId = " + this.nativeShellHolderId) + return 0; + } + return flutter.nativeGetTextureWindowId(this.nativeShellHolderId!, textureId); + } else { + return 0; + } + } + + setExternalNativeImage(textureId: number, native_image: number): boolean { + Log.d(TAG, "called setExternalNativeImage "); + if (this.isAttached()) { + if (!this.nativeShellHolderId) { + Log.e(TAG, "this.nativeShellHolderId = " + this.nativeShellHolderId) + return false; + } + return Boolean(flutter.nativeSetExternalNativeImage(this.nativeShellHolderId!, textureId, native_image)); + } else { + return false; + } + } + + resetExternalTexture(textureId: number, need_surfaceId: boolean): number { + Log.d(TAG, "called resetExternalTexture "); + if (this.isAttached()) { + if (!this.nativeShellHolderId) { + Log.e(TAG, "this.nativeShellHolderId = " + this.nativeShellHolderId) + return 0; + } + return flutter.nativeResetExternalTexture(this.nativeShellHolderId!, textureId, need_surfaceId); + } else { + return 0; + } + } + + setTextureBufferSize(textureId: number, width: number, height: number): void { + Log.d(TAG, "called setTextureBufferSize "); + if (!this.isAttached()) { + Log.e(TAG, "setTextureBufferSize this.nativeShellHolderId:" + this.nativeShellHolderId) + return; + } + flutter.nativeSetTextureBufferSize(this.nativeShellHolderId!, textureId, width, height); + } + + notifyTextureResizing(textureId: number, width: number, height: number): void { + Log.d(TAG, "called notifyTextureResizing "); + if (!this.isAttached()) { + Log.e(TAG, "notifyTextureResizing this.nativeShellHolderId:" + this.nativeShellHolderId) + return; + } + flutter.nativeNotifyTextureResizing(this.nativeShellHolderId!, textureId, width, height); + } + + enableFrameCache(enable: boolean): void { + if (!this.nativeShellHolderId) { + return; + } + flutter.nativeEnableFrameCache(this.nativeShellHolderId!, enable); + } + + /** Dispatch Touch Event */ + onTouchEvent(strings: Array): void { + if (this.isAttached()) { + TouchEventProcessor.getInstance().postTouchEvent(strings); + } + } + + static unicodeIsEmoji(code: number): boolean { + return Boolean(flutter.nativeUnicodeIsEmoji(code)); + } + + static unicodeIsEmojiModifier(code: number): boolean { + return Boolean(flutter.nativeUnicodeIsEmojiModifier(code)); + } + + static unicodeIsEmojiModifierBase(code: number): boolean { + return Boolean(flutter.nativeUnicodeIsEmojiModifierBase(code)); + } + + static unicodeIsVariationSelector(code: number): boolean { + return Boolean(flutter.nativeUnicodeIsVariationSelector(code)); + } + + static unicodeIsRegionalIndicatorSymbol(code: number): boolean { + return Boolean(flutter.nativeUnicodeIsRegionalIndicatorSymbol(code)); + } + + setFontWeightScale(fontWeightScale: number): void { + this.ensureRunningOnMainThread(); + if (this.isAttached()) { + Log.i(TAG, "setFontWeightScale: " + fontWeightScale); + flutter.nativeSetFontWeightScale(this.nativeShellHolderId!, fontWeightScale); + } else { + Log.w(TAG, "setFontWeightScale is detached !"); + } + } + + setFlutterNavigationAction(shellHolderId: number, isNavigate: boolean): void { + this.ensureRunningOnMainThread(); + if (this.isAttached()) { + Log.i(TAG, "setFlutterNavigationAction: " + isNavigate); + flutter.nativeSetFlutterNavigationAction(shellHolderId, isNavigate); + } else { + Log.w(TAG, "setFlutterNavigationAction is detached !"); + } + } +} + +export interface AccessibilityDelegate { + accessibilityStateChange(state: Boolean): void; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterOverlaySurface.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterOverlaySurface.ets new file mode 100644 index 0000000000000000000000000000000000000000..38cf84578c3ef2031e3fea4556c425373a577dd3 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterOverlaySurface.ets @@ -0,0 +1,26 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +export class FlutterOverlaySurface { + private id: number; + + constructor(id: number) { + this.id = id + } + + getId(): number { + return this.id; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterShellArgs.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterShellArgs.ets new file mode 100644 index 0000000000000000000000000000000000000000..c51aedc8ac24c59226e1ba0f447245f8d19ff3bf --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterShellArgs.ets @@ -0,0 +1,146 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import Want from '@ohos.app.ability.Want'; + +/** + * 封装flutter shell的参数 + */ +export default class FlutterShellArgs { + static ARG_KEY_TRACE_STARTUP = "trace-startup"; + static ARG_TRACE_STARTUP = "--trace-startup"; + static ARG_KEY_START_PAUSED = "start-paused"; + static ARG_START_PAUSED = "--start-paused"; + static ARG_KEY_DISABLE_SERVICE_AUTH_CODES = "disable-service-auth-codes"; + static ARG_DISABLE_SERVICE_AUTH_CODES = "--disable-service-auth-codes"; + static ARG_KEY_ENDLESS_TRACE_BUFFER = "endless-trace-buffer"; + static ARG_ENDLESS_TRACE_BUFFER = "--endless-trace-buffer"; + static ARG_KEY_USE_TEST_FONTS = "use-test-fonts"; + static ARG_USE_TEST_FONTS = "--use-test-fonts"; + static ARG_KEY_ENABLE_DART_PROFILING = "enable-dart-profiling"; + static ARG_ENABLE_DART_PROFILING = "--enable-dart-profiling"; + static ARG_KEY_ENABLE_SOFTWARE_RENDERING = "enable-software-rendering"; + static ARG_ENABLE_SOFTWARE_RENDERING = "--enable-software-rendering"; + static ARG_KEY_SKIA_DETERMINISTIC_RENDERING = "skia-deterministic-rendering"; + static ARG_SKIA_DETERMINISTIC_RENDERING = "--skia-deterministic-rendering"; + static ARG_KEY_TRACE_SKIA = "trace-skia"; + static ARG_TRACE_SKIA = "--trace-skia"; + static ARG_KEY_TRACE_SKIA_ALLOWLIST = "trace-skia-allowlist"; + static ARG_TRACE_SKIA_ALLOWLIST = "--trace-skia-allowlist="; + static ARG_KEY_TRACE_SYSTRACE = "trace-systrace"; + static ARG_TRACE_SYSTRACE = "--trace-systrace"; + static ARG_KEY_ENABLE_IMPELLER = "enable-impeller"; + static ARG_ENABLE_IMPELLER = "--enable-impeller"; + static ARG_KEY_DUMP_SHADER_SKP_ON_SHADER_COMPILATION = + "dump-skp-on-shader-compilation"; + static ARG_DUMP_SHADER_SKP_ON_SHADER_COMPILATION = + "--dump-skp-on-shader-compilation"; + static ARG_KEY_CACHE_SKSL = "cache-sksl"; + static ARG_CACHE_SKSL = "--cache-sksl"; + static ARG_KEY_PURGE_PERSISTENT_CACHE = "purge-persistent-cache"; + static ARG_PURGE_PERSISTENT_CACHE = "--purge-persistent-cache"; + static ARG_KEY_VERBOSE_LOGGING = "verbose-logging"; + static ARG_VERBOSE_LOGGING = "--verbose-logging"; + static ARG_KEY_OBSERVATORY_PORT = "observatory-port"; + static ARG_OBSERVATORY_PORT = "--observatory-port="; + static ARG_KEY_DART_FLAGS = "dart-flags"; + static ARG_DART_FLAGS = "--dart-flags="; + static ARG_KEY_MSAA_SAMPLES = "msaa-samples"; + static ARG_MSAA_SAMPLES = "--msaa-samples="; + + /** + * 从意图中解析参数,创建shellArgs + * @returns + */ + static fromWant(want: Want): FlutterShellArgs { + let flutterShellArgs: FlutterShellArgs = new FlutterShellArgs(); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_TRACE_STARTUP, FlutterShellArgs.ARG_TRACE_STARTUP, want, + flutterShellArgs); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_START_PAUSED, FlutterShellArgs.ARG_START_PAUSED, want, + flutterShellArgs); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_DISABLE_SERVICE_AUTH_CODES, + FlutterShellArgs.ARG_DISABLE_SERVICE_AUTH_CODES, want, flutterShellArgs); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_ENDLESS_TRACE_BUFFER, FlutterShellArgs.ARG_ENDLESS_TRACE_BUFFER, + want, flutterShellArgs); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_USE_TEST_FONTS, FlutterShellArgs.ARG_USE_TEST_FONTS, want, + flutterShellArgs); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_ENABLE_DART_PROFILING, + FlutterShellArgs.ARG_ENABLE_DART_PROFILING, want, flutterShellArgs); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_ENABLE_SOFTWARE_RENDERING, + FlutterShellArgs.ARG_ENABLE_SOFTWARE_RENDERING, want, flutterShellArgs); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_SKIA_DETERMINISTIC_RENDERING, + FlutterShellArgs.ARG_SKIA_DETERMINISTIC_RENDERING, want, flutterShellArgs); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_TRACE_SKIA, FlutterShellArgs.ARG_TRACE_SKIA, want, + flutterShellArgs); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_TRACE_SYSTRACE, FlutterShellArgs.ARG_TRACE_SYSTRACE, want, + flutterShellArgs); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_ENABLE_IMPELLER, FlutterShellArgs.ARG_ENABLE_IMPELLER, want, + flutterShellArgs); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_DUMP_SHADER_SKP_ON_SHADER_COMPILATION, + FlutterShellArgs.ARG_DUMP_SHADER_SKP_ON_SHADER_COMPILATION, want, flutterShellArgs); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_CACHE_SKSL, FlutterShellArgs.ARG_CACHE_SKSL, want, + flutterShellArgs); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_PURGE_PERSISTENT_CACHE, + FlutterShellArgs.ARG_PURGE_PERSISTENT_CACHE, want, flutterShellArgs); + FlutterShellArgs.checkArg(FlutterShellArgs.ARG_KEY_VERBOSE_LOGGING, FlutterShellArgs.ARG_VERBOSE_LOGGING, want, + flutterShellArgs); + + let skia_allow_list: Object = want.parameters![FlutterShellArgs.ARG_KEY_TRACE_SKIA_ALLOWLIST]; + if (skia_allow_list != undefined) { + flutterShellArgs.add(FlutterShellArgs.ARG_TRACE_SKIA_ALLOWLIST + (skia_allow_list as string)); + } + + let observatory_port: Object = want.parameters![FlutterShellArgs.ARG_KEY_OBSERVATORY_PORT]; + if (observatory_port != undefined && (observatory_port as number > 0)) { + flutterShellArgs.add(FlutterShellArgs.ARG_OBSERVATORY_PORT + (observatory_port as number)); + } + + let msaa: Object = want.parameters![FlutterShellArgs.ARG_KEY_MSAA_SAMPLES]; + if (msaa != undefined && (msaa as number > 1)) { + flutterShellArgs.add(FlutterShellArgs.ARG_MSAA_SAMPLES + (msaa as number)); + } + + let dart_flags: Object = want.parameters![FlutterShellArgs.ARG_KEY_DART_FLAGS]; + if (dart_flags != undefined) { + flutterShellArgs.add(FlutterShellArgs.ARG_DART_FLAGS + (msaa as string)); + } + return flutterShellArgs; + } + + static checkArg(argKey: string, argFlag: string, want: Want, flutterShellArgs: FlutterShellArgs) { + if (want.parameters == undefined) { + return; + } + let value: Object = want.parameters![argKey]; + if (value != undefined && value as Boolean) { + flutterShellArgs.add(argFlag); + } + } + + //参数 + args: Set = new Set(); + + add(arg: string) { + this.args.add(arg); + } + + remove(arg: string) { + this.args.delete(arg); + } + + toArray(): Array { + return Array.from(this.args); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/DartExecutor.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/DartExecutor.ets new file mode 100644 index 0000000000000000000000000000000000000000..4ecc57bb332743de18bd4bc1ef66e164d6b9e91e --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/DartExecutor.ets @@ -0,0 +1,358 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import resourceManager from '@ohos.resourceManager'; +import FlutterInjector from '../../../FlutterInjector'; +import { BinaryMessageHandler, BinaryReply, TaskQueue, TaskQueueOptions } from '../../../plugin/common/BinaryMessenger'; +import { BinaryMessenger } from '../../../plugin/common/BinaryMessenger'; +import StringCodec from '../../../plugin/common/StringCodec'; +import Log from '../../../util/Log'; +import { TraceSection } from '../../../util/TraceSection'; +import { FlutterCallbackInformation } from '../../../view/FlutterCallbackInformation'; +import FlutterNapi from '../FlutterNapi'; +import { DartMessenger } from './DartMessenger'; +import SendableBinaryMessageHandler from '../../../plugin/common/SendableBinaryMessageHandler' + + +const TAG = "DartExecutor"; + +/** + * dart代码执行器 + */ +export default class DartExecutor implements BinaryMessenger { + flutterNapi: FlutterNapi; + assetManager: resourceManager.ResourceManager; + private dartMessenger: DartMessenger; + private binaryMessenger: BinaryMessenger; + private isApplicationRunning: boolean = false; + private isolateServiceId: String = ""; + private isolateServiceIdListener: IsolateServiceIdListener | null = null; + private isolateChannelMessageHandler: BinaryMessageHandler = + new IsolateChannelMessageHandler(this.isolateServiceId, this.isolateServiceIdListener); + + constructor(flutterNapi: FlutterNapi, assetManager: resourceManager.ResourceManager) { + this.flutterNapi = flutterNapi; + this.assetManager = assetManager; + this.dartMessenger = new DartMessenger(flutterNapi); + this.dartMessenger.setMessageHandler("flutter/isolate", this.isolateChannelMessageHandler); + this.binaryMessenger = new DefaultBinaryMessenger(this.dartMessenger); + // The NAPI might already be attached if coming from a spawned engine. If so, correctly report + // that this DartExecutor is already running. + if (flutterNapi.isRunningDart) { + this.isApplicationRunning = true; + } + } + + + /** + * Invoked when the {@link io.flutter.embedding.engine.FlutterEngine} that owns this {@link + * DartExecutor} attaches to NAPI. + * + *

When attached to NAPI, this {@link DartExecutor} begins handling 2-way communication to/from + * the Dart execution context. This communication is facilitate via 2 APIs: + * + *

    + *
  • {@link BinaryMessenger}, which sends messages to Dart + *
  • {@link PlatformMessageHandler}, which receives messages from Dart + *
+ */ + onAttachedToNAPI(): void { + Log.d(TAG, "Attached to NAPI. Registering the platform message handler for this Dart execution context."); + this.flutterNapi.setPlatformMessageHandler(this.dartMessenger); + } + + /** + * Invoked when the {@link io.flutter.embedding.engine.FlutterEngine} that owns this {@link + * DartExecutor} detaches from NAPI. + * + *

When detached from NAPI, this {@link DartExecutor} stops handling 2-way communication to/from + * the Dart execution context. + */ + onDetachedFromNAPI(): void { + Log.d(TAG, "Detached from NAPI. De-registering the platform message handler for this Dart execution context."); + this.flutterNapi.setPlatformMessageHandler(null); + } + + /** + * Is this {@link DartExecutor} currently executing Dart code? + * + * @return true if Dart code is being executed, false otherwise + */ + isExecutingDart(): boolean { + return this.isApplicationRunning; + } + + /** + * Starts executing Dart code based on the given {@code dartEntrypoint} and the {@code + * dartEntrypointArgs}. + * + *

See {@link DartEntrypoint} for configuration options. + * + * @param dartEntrypoint specifies which Dart function to run, and where to find it + * @param dartEntrypointArgs Arguments passed as a list of string to Dart's entrypoint function. + */ + executeDartEntrypoint(dartEntrypoint: DartEntrypoint, dartEntrypointArgs?: string[]): void { + if (this.isApplicationRunning) { + Log.w(TAG, "Attempted to run a DartExecutor that is already running."); + return; + } + + let traceId: number = TraceSection.begin("DartExecutor#executeDartEntrypoint"); + try { + Log.d(TAG, "Executing Dart entrypoint: " + dartEntrypoint); + this.flutterNapi.runBundleAndSnapshotFromLibrary( + dartEntrypoint.pathToBundle, + dartEntrypoint.dartEntrypointFunctionName, + dartEntrypoint.dartEntrypointLibrary, + this.assetManager, + dartEntrypointArgs ?? []); + + this.isApplicationRunning = true; + } finally { + TraceSection.endWithId("DartExecutor#executeDartEntrypoint", traceId); + } + } + + /** + * Starts executing Dart code based on the given {@code dartCallback}. + * + *

See {@link DartCallback} for configuration options. + * + * @param dartCallback specifies which Dart callback to run, and where to find it + */ + executeDartCallback(dartCallback: DartCallback): void { + if (this.isApplicationRunning) { + Log.w(TAG, "Attempted to run a DartExecutor that is already running."); + return; + } + + let traceId: number = TraceSection.begin("DartExecutor#executeDartCallback"); + try { + Log.d(TAG, "Executing Dart callback: " + dartCallback); + this.flutterNapi.runBundleAndSnapshotFromLibrary( + dartCallback.pathToBundle, + dartCallback.callbackHandle.callbackName, + dartCallback.callbackHandle.callbackLibraryPath, + dartCallback.resourceManager, + []); + + this.isApplicationRunning = true; + } finally { + TraceSection.endWithId("DartExecutor#executeDartCallback", traceId); + } + } + + /** + * Returns a {@link BinaryMessenger} that can be used to send messages to, and receive messages + * from, Dart code that this {@code DartExecutor} is executing. + */ + + getBinaryMessenger(): BinaryMessenger { + return this.binaryMessenger; + } + + makeBackgroundTaskQueue(options?: TaskQueueOptions): TaskQueue { + return this.getBinaryMessenger().makeBackgroundTaskQueue(options); + } + + + send(channel: String, message: ArrayBuffer, callback?: BinaryReply): void { + this.getBinaryMessenger().send(channel, message, callback); + } + + setMessageHandler(channel: String, handler: BinaryMessageHandler | SendableBinaryMessageHandler | null, + taskQueue?: TaskQueue, ...args: Object[]): void { + this.getBinaryMessenger().setMessageHandler(channel, handler, taskQueue, ...args); + } + + /** + * Returns the number of pending channel callback replies. + * + *

When sending messages to the Flutter application using {@link BinaryMessenger#send(String, + * ByteBuffer, io.flutter.plugin.common.BinaryMessenger.BinaryReply)}, developers can optionally + * specify a reply callback if they expect a reply from the Flutter application. + * + *

This method tracks all the pending callbacks that are waiting for response, and is supposed + * to be called from the main thread (as other methods). Calling from a different thread could + * possibly capture an indeterministic internal state, so don't do it. + * + *

Currently, it's mainly useful for a testing framework like Espresso to determine whether all + * the async channel callbacks are handled and the app is idle. + */ + getPendingChannelResponseCount(): number { + return this.dartMessenger.getPendingChannelResponseCount(); + } + + /** + * Returns an identifier for this executor's primary isolate. This identifier can be used in + * queries to the Dart service protocol. + */ + + getIsolateServiceId(): String { + return this.isolateServiceId; + } + + + /** + * Set a listener that will be notified when an isolate identifier is available for this + * executor's primary isolate. + */ + setIsolateServiceIdListener(listener: IsolateServiceIdListener): void { + this.isolateServiceIdListener = listener; + if (this.isolateServiceIdListener != null && this.isolateServiceId != null) { + this.isolateServiceIdListener.onIsolateServiceIdAvailable(this.isolateServiceId); + } + } + + /** + * Notify the Dart VM of a low memory event, or that the application is in a state such that now + * is an appropriate time to free resources, such as going to the background. + * + *

This does not notify a Flutter application about memory pressure. For that, use the {@link + * io.flutter.embedding.engine.systemchannels.SystemChannel#sendMemoryPressureWarning}. + * + *

Calling this method may cause jank or latency in the application. Avoid calling it during + * critical periods like application startup or periods of animation. + */ + notifyLowMemoryWarning(): void { + if (this.flutterNapi.isAttached()) { + this.flutterNapi.notifyLowMemoryWarning(); + } + } +} + + +/** + * Configuration options that specify which Dart entrypoint function is executed and where to find + * that entrypoint and other assets required for Dart execution. + */ +export class DartEntrypoint { + /** The path within the AssetManager where the app will look for assets. */ + pathToBundle: string; + /** The library or file location that contains the Dart entrypoint function. */ + dartEntrypointLibrary: string; + /** The name of a Dart function to execute. */ + dartEntrypointFunctionName: string; + + constructor(pathToBundle: string, + dartEntrypointLibrary: string, + dartEntrypointFunctionName: string) { + this.pathToBundle = pathToBundle; + this.dartEntrypointLibrary = dartEntrypointLibrary; + this.dartEntrypointFunctionName = dartEntrypointFunctionName; + } + + static createDefault() { + const flutterLoader = FlutterInjector.getInstance().getFlutterLoader(); + if (!flutterLoader.initialized) { + throw new Error( + "DartEntrypoints can only be created once a FlutterEngine is created."); + } + return new DartEntrypoint(flutterLoader.findAppBundlePath(), "", "main"); + } +} + + +/** Callback interface invoked when the isolate identifier becomes available. */ +interface IsolateServiceIdListener { + onIsolateServiceIdAvailable(isolateServiceId: String): void; +} + + +/** + * Configuration options that specify which Dart callback function is executed and where to find + * that callback and other assets required for Dart execution. + */ +export class DartCallback { + /** Standard HarmonyOS AssetManager, provided from some {@code Context} or {@code Resources}. */ + public resourceManager: resourceManager.ResourceManager; + /** The path within the AssetManager where the app will look for assets. */ + public pathToBundle: string; + /** A Dart callback that was previously registered with the Dart VM. */ + public callbackHandle: FlutterCallbackInformation; + + constructor(resourceManager: resourceManager.ResourceManager, + pathToBundle: string, + callbackHandle: FlutterCallbackInformation) { + this.resourceManager = resourceManager; + this.pathToBundle = pathToBundle; + this.callbackHandle = callbackHandle; + } + + toString(): String { + return "DartCallback( bundle path: " + + this.pathToBundle + + ", library path: " + + this.callbackHandle.callbackLibraryPath + + ", function: " + + this.callbackHandle.callbackName + + " )"; + } +} + +export class DefaultBinaryMessenger implements BinaryMessenger { + private messenger: DartMessenger; + + constructor(messenger: DartMessenger) { + this.messenger = messenger; + } + + makeBackgroundTaskQueue(options?: TaskQueueOptions): TaskQueue { + return this.messenger.makeBackgroundTaskQueue(options); + } + + /** + * Sends the given {@code messages} from HarmonyOS to Dart over the given {@code channel} and then + * has the provided {@code callback} invoked when the Dart side responds. + * + * @param channel the name of the logical channel used for the message. + * @param message the message payload, a direct-allocated {@link ByteBuffer} with the message + * bytes between position zero and current position, or null. + * @param callback a callback invoked when the Dart application responds to the message + */ + + send(channel: String, message: ArrayBuffer, callback?: BinaryReply): void { + this.messenger.send(channel, message, callback); + } + + /** + * Sets the given {@link io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler} as the + * singular handler for all incoming messages received from the Dart side of this Dart execution + * context. + * + * @param channel the name of the channel. + * @param handler a {@link BinaryMessageHandler} to be invoked on incoming messages, or null. + */ + setMessageHandler(channel: String, handler: BinaryMessageHandler | SendableBinaryMessageHandler | null, + taskQueue?: TaskQueue, ...args: Object[]): void { + this.messenger.setMessageHandler(channel, handler, taskQueue, ...args); + } +} + +class IsolateChannelMessageHandler implements BinaryMessageHandler { + private isolateServiceId: String; + private isolateServiceIdListener: IsolateServiceIdListener | null = null; + + constructor(isolateServiceId: String, isolateServiceIdListener: IsolateServiceIdListener | null) { + this.isolateServiceId = isolateServiceId; + this.isolateServiceIdListener = isolateServiceIdListener; + } + + onMessage(message: ArrayBuffer, callback: BinaryReply): void { + this.isolateServiceId = StringCodec.INSTANCE.decodeMessage(message); + if (this.isolateServiceIdListener != null) { + this.isolateServiceIdListener.onIsolateServiceIdAvailable(this.isolateServiceId); + } + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/DartMessenger.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/DartMessenger.ets new file mode 100644 index 0000000000000000000000000000000000000000..14a93fd37514305c0743c4f68fdf8c950b2775ae --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/DartMessenger.ets @@ -0,0 +1,365 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import { ErrorEvent, Queue, taskpool, worker, MessageEvents, JSON } from '@kit.ArkTS'; + +import Log from '../../../util/Log'; +import { + BinaryMessageHandler, + BinaryMessenger, + BinaryReply, + TaskPriority, + TaskQueue, + TaskQueueOptions +} from '../../../plugin/common/BinaryMessenger'; +import FlutterNapi from '../FlutterNapi'; +import { PlatformMessageHandler } from './PlatformMessageHandler'; +import { TraceSection } from '../../../util/TraceSection'; +import SendableBinaryMessageHandler from '../../../plugin/common/SendableBinaryMessageHandler' + +/** + * Message conduit for 2-way communication between HarmonyOS and Dart. + * + *

See {@link BinaryMessenger}, which sends messages from HarmonyOS to Dart + * + *

See {@link PlatformMessageHandler}, which handles messages to HarmonyOS from Dart + */ + +const TAG = "DartMessenger"; + +export class DartMessenger implements BinaryMessenger, PlatformMessageHandler { + flutterNapi: FlutterNapi; + /** + * Maps a channel name to an object that contains the task queue and the handler associated with + * the channel. + */ + messageHandlers: Map = new Map(); + pendingReplies: Map = new Map(); + nextReplyId: number = 1; + taskQueueFactory: TaskQueueFactory; + createdTaskQueues: Map = new Map(); + + constructor(flutterNapi: FlutterNapi) { + this.flutterNapi = flutterNapi; + this.taskQueueFactory = new DefaultTaskQueueFactory(); + } + + makeBackgroundTaskQueue(options?: TaskQueueOptions): TaskQueue { + let taskQueue: DartMessengerTaskQueue = + this.taskQueueFactory.makeBackgroundTaskQueue(options ?? new TaskQueueOptions()); + let token: TaskQueueToken = new TaskQueueToken(); + this.createdTaskQueues.set(token, taskQueue); + return token; + } + + setMessageHandler(channel: String, handler: BinaryMessageHandler | SendableBinaryMessageHandler | null, + taskQueue?: TaskQueue, ...args: Object[]): void { + if (handler == null) { + Log.d(TAG, "Removing handler for channel '" + channel + "'"); + this.messageHandlers.delete(channel); + return; + } + let dartMessengerTaskQueue: DartMessengerTaskQueue | null = null; + if (taskQueue !== null && taskQueue !== undefined) { + dartMessengerTaskQueue = this.createdTaskQueues.get(taskQueue) ?? null; + if (dartMessengerTaskQueue == null) { + throw new Error( + "Unrecognized TaskQueue, use BinaryMessenger to create your TaskQueue (ex makeBackgroundTaskQueue)." + ); + } + } + Log.d(TAG, "Setting handler for channel '" + channel + "'"); + + this.messageHandlers.set(channel, new HandlerInfo(handler, dartMessengerTaskQueue, ...args)); + } + + send(channel: String, message: ArrayBuffer, callback?: BinaryReply): void { + Log.d(TAG, "Sending message over channel '" + channel + "'"); + let traceId: number = TraceSection.begin("DartMessenger#send on " + channel); + try { + Log.d(TAG, "Sending message with callback over channel '" + channel + "'"); + let replyId: number = this.nextReplyId++; + if (callback != null) { + this.pendingReplies.set(replyId, callback); + } + if (message == null) { + this.flutterNapi.dispatchEmptyPlatformMessage(channel, replyId); + } else { + this.flutterNapi.dispatchPlatformMessage(channel, message, message.byteLength, replyId); + } + } finally { + TraceSection.endWithId("DartMessenger#send on " + channel, traceId); + } + this.IsFlutterNavigationExecuted(channel); + } + + dispatchMessageToQueue(handlerInfo: HandlerInfo, message: ArrayBuffer, replyId: number): void { + let taskState: TaskState = new TaskState(handlerInfo.handler as ESObject, message, ...handlerInfo.args); + handlerInfo.taskQueue?.dispatch(taskState, new Reply(this.flutterNapi, replyId)); + } + + invokeHandler(handler: BinaryMessageHandler | null, message: ArrayBuffer, replyId: number): void { + if (handler != null) { + try { + Log.d(TAG, "Deferring to registered handler to process message."); + handler.onMessage(message, new Reply(this.flutterNapi, replyId)); + } catch (ex) { + Log.e(TAG, "Uncaught exception in binary message listener", ex); + this.flutterNapi.invokePlatformMessageEmptyResponseCallback(replyId); + } + } else { + Log.d(TAG, "No registered handler for message. Responding to Dart with empty reply message."); + this.flutterNapi.invokePlatformMessageEmptyResponseCallback(replyId); + } + } + + handleMessageFromDart(channel: String, message: ArrayBuffer, replyId: number, messageData: number): void { + Log.d(TAG, "Received message from Dart over channel '" + channel + "'"); + let handlerInfo: HandlerInfo | null = this.messageHandlers.get(channel) ?? null; + if (handlerInfo?.taskQueue != null) { + this.dispatchMessageToQueue(handlerInfo, message, replyId); + } else { + this.invokeHandler(handlerInfo?.handler as BinaryMessageHandler, message, replyId); + } + this.IsFlutterNavigationExecuted(channel); + } + + handlePlatformMessageResponse(replyId: number, reply: ArrayBuffer): void { + Log.d(TAG, "Received message reply from Dart."); + let callback: BinaryReply | null = this.pendingReplies.get(replyId) ?? null; + this.pendingReplies.delete(replyId); + if (callback != null) { + try { + Log.d(TAG, "Invoking registered callback for reply from Dart."); + callback.reply(reply); + } catch (e) { + Log.e(TAG, "Uncaught exception in binary message reply handler", e); + } + } + } + + /** + * Returns the number of pending channel callback replies. + * + *

When sending messages to the Flutter application using {@link BinaryMessenger#send(String, + * ByteBuffer, io.flutter.plugin.common.BinaryMessenger.BinaryReply)}, developers can optionally + * specify a reply callback if they expect a reply from the Flutter application. + * + *

This method tracks all the pending callbacks that are waiting for response, and is supposed + * to be called from the main thread (as other methods). Calling from a different thread could + * possibly capture an indeterministic internal state, so don't do it. + */ + getPendingChannelResponseCount(): number { + return this.pendingReplies.size; + } + + //获取当前flutter页面是否路由跳转,并传递到native侧 + IsFlutterNavigationExecuted(channel: String): void { + if (channel == "flutter/navigation") { + this.flutterNapi.setFlutterNavigationAction(this.flutterNapi.nativeShellHolderId!, true); + Log.d(TAG, "setFlutterNavigationAction -> '" + channel + "'"); + } + } +} + +/** + * Holds information about a platform handler, such as the task queue that processes messages from + * Dart. + */ +class HandlerInfo { + handler: BinaryMessageHandler | SendableBinaryMessageHandler; + taskQueue: DartMessengerTaskQueue | null; + args: Object[]; + + constructor(handler: BinaryMessageHandler | SendableBinaryMessageHandler, + taskQueue: DartMessengerTaskQueue | null, + ...args: Object[]) { + this.handler = handler; + this.taskQueue = taskQueue; + this.args = args; + } +} + +class Reply implements BinaryReply { + flutterNapi: FlutterNapi; + replyId: number; + done: boolean = false; + + constructor(flutterNapi: FlutterNapi, replyId: number) { + this.flutterNapi = flutterNapi; + this.replyId = replyId; + } + + reply(reply: ArrayBuffer | null) { + if (this.done) { + throw new Error("Reply already submitted"); + } + + if (reply == null) { + this.flutterNapi.invokePlatformMessageEmptyResponseCallback(this.replyId); + } else { + this.flutterNapi.invokePlatformMessageResponseCallback(this.replyId, reply, reply.byteLength); + } + } +} + +export class TaskState { + handler: SendableBinaryMessageHandler; + message: ArrayBuffer; + args: Object[]; + + constructor(handler: SendableBinaryMessageHandler, message: ArrayBuffer, ...args: Object[]) { + this.handler = handler; + this.message = message; + this.args = args; + } +} + +interface DartMessengerTaskQueue { + dispatch(taskState: TaskState, callback: Reply): void; +} + +interface SerialTaskQueue extends DartMessengerTaskQueue { +} + +interface TaskQueueFactory { + makeBackgroundTaskQueue(options: TaskQueueOptions): DartMessengerTaskQueue; +} + +class ConcurrentTaskQueue implements DartMessengerTaskQueue { + private priority: TaskPriority; + + constructor(priority: TaskPriority) { + this.priority = priority; + } + + dispatch(taskState: TaskState, callback: Reply): void { + let task: taskpool.Task = new taskpool.Task(handleMessageInBackground, + taskState.handler, + taskState.message, + ...taskState.args); + taskpool.execute(task, this.priority as number).then((result: Object) => { + callback.reply(result as ArrayBuffer); + }).catch((err: string) => { + callback.reply(null); + Log.e(TAG, "Oops! Failed to execute task: ", err); + }); + } +} + +const scriptURL: string = '../workers/PlatformChannelWorker.ets'; +class SerialTaskQueueWithWorker implements SerialTaskQueue { + private static workerInstance: worker.ThreadWorker | null = null; + + constructor () { + if (!SerialTaskQueueWithWorker.workerInstance) { + SerialTaskQueueWithWorker.workerInstance = + new worker.ThreadWorker(scriptURL, {name: 'PlatformChannelWorker'}); + } + } + + dispatch(taskState: TaskState, callback: Reply): void { + SerialTaskQueueWithWorker.workerInstance!.onmessage = (e: MessageEvents): void => { + callback.reply(e.data as ArrayBuffer); + } + + SerialTaskQueueWithWorker.workerInstance!.onerror = (err: ErrorEvent) => { + callback.reply(null); + Log.e(TAG, "Oops! Failed to execute task in worker thread: ", err.message); + } + + SerialTaskQueueWithWorker.workerInstance!.postMessageWithSharedSendable(taskState, [taskState.message]); + } +} + +type Runnable = () => Promise; +class SerialTaskQueueWithTaskPool implements SerialTaskQueue { + private priority: TaskPriority; + private queue: Queue = new Queue(); + private isRunning: boolean = false; + + constructor(priority: TaskPriority) { + this.priority = priority; + } + + dispatch(taskState: TaskState, callback: Reply): void { + let task: taskpool.Task = new taskpool.Task(handleMessageInBackground, + taskState.handler, + taskState.message, + ...taskState.args); + const runnable: Runnable = async () => { + try { + const result = await taskpool.execute(task, this.priority as number); + callback.reply(result as ArrayBuffer); + } catch (err) { + callback.reply(null); + Log.e(TAG, "Oops! Failed to execute task: ", err); + } + }; + + this.queue.add(runnable); + + if (!this.isRunning) { + this.runNext(); + } + } + + private async runNext(): Promise { + if (this.queue.length > 0) { + this.isRunning = true; + const task = this.queue.pop(); + try { + await task(); + } finally { + this.isRunning = false; + this.runNext(); // 执行下一个任务 + } + } + } +} + +class DefaultTaskQueueFactory implements TaskQueueFactory { + makeBackgroundTaskQueue(options: TaskQueueOptions): DartMessengerTaskQueue { + if (options.isSingleThreadMode()) { + return new SerialTaskQueueWithWorker(); + } else { + if (options.getIsSerial()) { + return new SerialTaskQueueWithTaskPool(options.getPriority()); + } + return new ConcurrentTaskQueue(options.getPriority()); + } + } +} + +class TaskQueueToken implements TaskQueue { +} + +@Concurrent +async function handleMessageInBackground(handler: SendableBinaryMessageHandler, + message: ArrayBuffer, + ...args: Object[]): Promise { + const result = await new Promise((resolve, reject) => { + try { + handler.onMessage(message, { + reply: (reply: ArrayBuffer | null): void => { + resolve(reply); + } + }, ...args); + } catch (e) { + reject(null); + Log.e('WARNING', "Oops! Failed to handle message in the background: ", e); + } + }); + return result; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/PlatformMessageHandler.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/PlatformMessageHandler.ets new file mode 100644 index 0000000000000000000000000000000000000000..a963ffcca098e244b7ae8d54c4d579ce84b999c7 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/PlatformMessageHandler.ets @@ -0,0 +1,22 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +export interface PlatformMessageHandler { + + handleMessageFromDart(channel: String, message: ArrayBuffer, replyId: number, messageData: number): void; + + handlePlatformMessageResponse(replyId: number, reply: ArrayBuffer): void; + +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/loader/ApplicationInfoLoader.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/loader/ApplicationInfoLoader.ets new file mode 100644 index 0000000000000000000000000000000000000000..35f3c73c402071a3db18c6f31a74a76e66cbea35 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/loader/ApplicationInfoLoader.ets @@ -0,0 +1,25 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import FlutterApplicationInfo from './FlutterApplicationInfo'; +import common from '@ohos.app.ability.common'; + +export default class ApplicationInfoLoader { + static load(context: common.Context) { + let applicationInfo = + new FlutterApplicationInfo(null, null, null, null, null, context.bundleCodeDir + '/libs/arm64', true); + return applicationInfo + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/loader/FlutterApplicationInfo.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/loader/FlutterApplicationInfo.ets new file mode 100644 index 0000000000000000000000000000000000000000..6ac0604d9d942f7087e2ff6891a781a573193fb4 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/loader/FlutterApplicationInfo.ets @@ -0,0 +1,57 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import BuildProfile from "../../../../../../BuildProfile"; + +const DEFAULT_AOT_SHARED_LIBRARY_NAME = "libapp.so"; +const DEFAULT_VM_SNAPSHOT_DATA = "vm_snapshot_data"; +const DEFAULT_ISOLATE_SNAPSHOT_DATA = "isolate_snapshot_data"; +const DEFAULT_FLUTTER_ASSETS_DIR = "flutter_assets"; + + +/** + * application 信息,后期看如何设置 + */ +export default class FlutterApplicationInfo { + aotSharedLibraryName: string; + vmSnapshotData: string; + isolateSnapshotData: string; + flutterAssetsDir: string; + domainNetworkPolicy: string; + nativeLibraryDir: string; + automaticallyRegisterPlugins: boolean; + //是否是开发模式,先放在这里,后续应该从context获取 + isDebugMode: boolean; + //是否是profile模式 + isProfile: boolean; + + constructor(aotSharedLibraryName: string | null, + vmSnapshotData: string | null, + isolateSnapshotData: string | null, + flutterAssetsDir: string | null, + domainNetworkPolicy: string | null, + nativeLibraryDir: string, + automaticallyRegisterPlugins: boolean) { + this.aotSharedLibraryName = aotSharedLibraryName == null ? DEFAULT_AOT_SHARED_LIBRARY_NAME : aotSharedLibraryName; + this.vmSnapshotData = vmSnapshotData == null ? DEFAULT_VM_SNAPSHOT_DATA : vmSnapshotData; + this.isolateSnapshotData = isolateSnapshotData == null ? DEFAULT_ISOLATE_SNAPSHOT_DATA : isolateSnapshotData; + this.flutterAssetsDir = flutterAssetsDir == null ? DEFAULT_FLUTTER_ASSETS_DIR : flutterAssetsDir; + this.domainNetworkPolicy = domainNetworkPolicy == null ? "" : domainNetworkPolicy; + this.nativeLibraryDir = nativeLibraryDir; + this.automaticallyRegisterPlugins = automaticallyRegisterPlugins; + this.isDebugMode = "debug" == String(BuildProfile.BUILD_MODE_NAME); + this.isProfile = "profile" == String(BuildProfile.BUILD_MODE_NAME); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/loader/FlutterLoader.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/loader/FlutterLoader.ets new file mode 100644 index 0000000000000000000000000000000000000000..83b7bfb0facb9d8c115735f7bc9417d1882c4eaf --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/loader/FlutterLoader.ets @@ -0,0 +1,343 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +/** + * flutterLoader,负责dart虚拟机启动和dart代码加载 + */ +import FlutterShellArgs from '../FlutterShellArgs'; +import FlutterNapi from '../FlutterNapi'; +import Log from '../../../util/Log'; +import FlutterApplicationInfo from './FlutterApplicationInfo'; +import common from '@ohos.app.ability.common'; +import StringUtils from '../../../util/StringUtils'; +import ApplicationInfoLoader from './ApplicationInfoLoader'; +import bundleManager from '@ohos.bundle.bundleManager'; +import fs from '@ohos.file.fs'; +import { BusinessError } from '@ohos.base'; +import data_preferences from '@ohos.data.preferences'; +import { util } from '@kit.ArkTS'; +import deviceInfo from '@ohos.deviceInfo'; + +const TAG = "FlutterLoader"; + +//flutter引擎so +const DEFAULT_LIBRARY = "libflutter.so"; +//jit产物默认kenel文件 +const DEFAULT_KERNEL_BLOB = "kernel_blob.bin"; +//jit产物,默认快照文件 +const VMSERVICE_SNAPSHOT_LIBRARY = "libvmservice_snapshot.so"; +//key值 +const SNAPSHOT_ASSET_PATH_KEY = "snapshot-asset-path"; +//key值 +const VM_SNAPSHOT_DATA_KEY = "vm-snapshot-data"; +//key值 +const ISOLATE_SNAPSHOT_DATA_KEY = "isolate-snapshot-data"; + + +const AOT_SHARED_LIBRARY_NAME = "aot-shared-library-name"; + +const AOT_VMSERVICE_SHARED_LIBRARY_NAME = "aot-vmservice-shared-library-name"; + +//文件路径分隔符 +const FILE_SEPARATOR = "/"; + +const TIMESTAMP_PREFIX = "res_timestamp-"; + +const ENABLE_IMPELLER_TAG = "enable_impeller"; + +const TRUE_STRING = "true"; + +const BUILD_INFO_FILE_NAME = "buildinfo.json5"; + +interface StringItem { + name: string; + value: string; +} + +interface InfoData { + string: StringItem[]; +} + +async function prefetchDefaultFontManager(): Promise { + await new Promise((resolve: Function) => { + FlutterNapi.prefetchDefaultFontManager() + resolve() + }) +} + +/** + * 定位在hap包中的flutter资源,并且加载flutter native library. + */ +export default class FlutterLoader { + flutterNapi: FlutterNapi; + initResult: InitResult | null = null; + flutterApplicationInfo: FlutterApplicationInfo | null = null; + context: common.Context | null = null; + initialized: boolean = false; + //初始化开始时间戳 + initStartTimestampMillis: number = 0; + isEnableImpeller: boolean = false; + + constructor(flutterNapi: FlutterNapi) { + this.flutterNapi = flutterNapi; + } + + private getBuildInfo(context: common.Context): Map { + let buildInfoMap: Map = new Map(); + try { + let rawFile = context.resourceManager.getRawFileContentSync(BUILD_INFO_FILE_NAME); + let textDecoder = util.TextDecoder.create('utf-8', { + ignoreBOM: true + }); + let record = textDecoder.decodeWithStream(rawFile, { + stream: false + }); + let jsonRecord: InfoData = JSON.parse(record); + jsonRecord.string.forEach((item: StringItem) => { + buildInfoMap.set(item.name, item.value); + }); + return buildInfoMap; + } catch (error) { + Log.e(TAG, "can not find buildinfo.json5 file.") + return buildInfoMap; + } + + } + + /** + * Starts initialization of the native system. + * + *

This loads the Flutter engine's native library to enable subsequent NAPI calls. This also + * starts locating and unpacking Dart resources packaged in the app's HAP. + * + *

Calling this method multiple times has no effect. + * + * @param applicationContext The HarmonyOS application context. + * @param settings Configuration settings. + */ + startInitialization(context: common.Context) { + Log.d(TAG, "flutterLoader start init") + this.initStartTimestampMillis = Date.now(); + this.context = context; + this.flutterApplicationInfo = ApplicationInfoLoader.load(context); + prefetchDefaultFontManager(); + if (this.flutterApplicationInfo!.isDebugMode) { + this.copyResource(context) + } + let buildInfoMap = this.getBuildInfo(this.context!); + if (!buildInfoMap.has(ENABLE_IMPELLER_TAG) || buildInfoMap.get(ENABLE_IMPELLER_TAG) == TRUE_STRING) { + this.isEnableImpeller = true; + } else { + this.isEnableImpeller = false; + } + this.initResult = new InitResult( + `${context.filesDir}/`, + `${context.cacheDir}/`, + `${context.filesDir}` + ) + Log.d(TAG, "flutterLoader end init") + } + + private copyResource(context: common.Context) { + let filePath = context.filesDir + FILE_SEPARATOR + this.flutterApplicationInfo!.flutterAssetsDir + const timestamp = this.checkTimestamp(filePath); + if (timestamp == null) { + Log.d(TAG, "no need copyResource") + return; + } + if (this.context != null) { + Log.d(TAG, "start copyResource") + if (fs.accessSync(filePath + FILE_SEPARATOR + DEFAULT_KERNEL_BLOB)) { + Log.d(TAG, "hap has changed, start delete previous file") + fs.rmdirSync(filePath); + } + + if (!fs.accessSync(filePath)) { + fs.mkdirSync(filePath) + } + + let kernelBuffer = + this.context.resourceManager.getRawFileContentSync(this.flutterApplicationInfo!.flutterAssetsDir + + FILE_SEPARATOR + DEFAULT_KERNEL_BLOB) + let kernelFile = + fs.openSync(filePath + FILE_SEPARATOR + DEFAULT_KERNEL_BLOB, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE) + fs.writeSync(kernelFile.fd, kernelBuffer.buffer) + + let vmBuffer = + this.context.resourceManager.getRawFileContentSync(this.flutterApplicationInfo!.flutterAssetsDir + + FILE_SEPARATOR + this.flutterApplicationInfo!.vmSnapshotData) + let vmFile = fs.openSync(filePath + FILE_SEPARATOR + this.flutterApplicationInfo!.vmSnapshotData, + fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE) + fs.writeSync(vmFile.fd, vmBuffer.buffer) + + let isolateBuffer = + this.context.resourceManager.getRawFileContentSync(this.flutterApplicationInfo!.flutterAssetsDir + + FILE_SEPARATOR + this.flutterApplicationInfo!.isolateSnapshotData) + let isolateFile = fs.openSync(filePath + FILE_SEPARATOR + this.flutterApplicationInfo!.isolateSnapshotData, + fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE) + fs.writeSync(isolateFile.fd, isolateBuffer.buffer) + + if (timestamp != null) { + fs.closeSync(fs.openSync(filePath + FILE_SEPARATOR + timestamp, fs.OpenMode.READ_ONLY | fs.OpenMode.CREATE)) + } + fs.closeSync(kernelFile) + fs.closeSync(vmFile) + fs.closeSync(isolateFile) + Log.d(TAG, "copyResource end") + } else { + Log.d(TAG, "no copyResource") + } + } + + /** + * 初始化dart虚拟机方法 + * @param flutterShellArgs + */ + ensureInitializationComplete(shellArgs: Array | null) { + if (this.initialized) { + return; + } + if (shellArgs == null) { + shellArgs = new Array(); + } + shellArgs.push("--icu-symbol-prefix=_binary_icudtl_dat"); + shellArgs.push( + "--icu-native-lib-path=" + + this.flutterApplicationInfo!.nativeLibraryDir + + FILE_SEPARATOR + DEFAULT_LIBRARY + ); + + let kernelPath: string = ""; + if (this.flutterApplicationInfo!.isDebugMode) { + Log.d(TAG, "this.initResult!.dataDirPath=" + this.initResult!.dataDirPath) + const snapshotAssetPath = + this.initResult!.dataDirPath + FILE_SEPARATOR + this.flutterApplicationInfo!.flutterAssetsDir; + kernelPath = snapshotAssetPath + FILE_SEPARATOR + DEFAULT_KERNEL_BLOB; + shellArgs.push("--" + SNAPSHOT_ASSET_PATH_KEY + "=" + snapshotAssetPath); + shellArgs.push("--" + VM_SNAPSHOT_DATA_KEY + "=" + this.flutterApplicationInfo!.vmSnapshotData); + shellArgs.push( + "--" + ISOLATE_SNAPSHOT_DATA_KEY + "=" + this.flutterApplicationInfo!.isolateSnapshotData); + shellArgs.push('--enable-checked-mode') + shellArgs.push('--verbose-logging') + } else { + shellArgs.push( + "--" + AOT_SHARED_LIBRARY_NAME + "=" + this.flutterApplicationInfo!.aotSharedLibraryName); + shellArgs.push( + "--" + + AOT_SHARED_LIBRARY_NAME + + "=" + + this.flutterApplicationInfo!.nativeLibraryDir + + FILE_SEPARATOR + + this.flutterApplicationInfo!.aotSharedLibraryName); + + const snapshotAssetPath = + this.initResult!.dataDirPath + FILE_SEPARATOR + this.flutterApplicationInfo!.flutterAssetsDir; + + if (this.flutterApplicationInfo!.isProfile) { + shellArgs.push("--" + AOT_VMSERVICE_SHARED_LIBRARY_NAME + "=" + VMSERVICE_SNAPSHOT_LIBRARY); + } + } + shellArgs.push("--cache-dir-path=" + this.initResult!.engineCachesPath); + if (StringUtils.isNotEmpty(this.flutterApplicationInfo!.domainNetworkPolicy)) { + shellArgs.push("--domain-network-policy=" + this.flutterApplicationInfo!.domainNetworkPolicy); + } + + const resourceCacheMaxBytesThreshold = 1080 * 1920 * 12 * 4; + shellArgs.push("--resource-cache-max-bytes-threshold=" + resourceCacheMaxBytesThreshold); + + shellArgs.push("--prefetched-default-font-manager"); + + shellArgs.push("--leak-vm=" + true); + + if (this.isEnableImpeller == true && deviceInfo.productModel != "emulator") { + shellArgs.push("--enable-impeller"); + Log.d(TAG, "Enable Impeller in Ohos."); + } else { + Log.d(TAG, "Do not find enableImpeller tag or enableImpeller tag set to false, enable Skia in Ohos."); + } + + // //最终初始化操作 + const costTime = Date.now() - this.initStartTimestampMillis; + this.flutterNapi.init( + this.context!, + shellArgs, + kernelPath, + this.initResult!.appStoragePath, + this.initResult!.engineCachesPath!, + costTime + ); + this.initialized = true; + Log.d(TAG, "ensureInitializationComplete") + } + + findAppBundlePath(): string { + return this.flutterApplicationInfo == null ? "" : this.flutterApplicationInfo!.flutterAssetsDir; + } + + getLookupKeyForAsset(asset: string, packageName?: string): string { + return this.fullAssetPathFrom(asset); + } + + fullAssetPathFrom(filePath: string): string { + return this.flutterApplicationInfo == null ? "" : this.flutterApplicationInfo!.flutterAssetsDir + "/" + filePath; + } + + private checkTimestamp(dataDir: string): string | null { + let bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT); + const expectedTimestamp = TIMESTAMP_PREFIX + bundleInfo.versionCode + "-" + bundleInfo.updateTime; + const existingTimestamps = this.getExistingTimestamps(dataDir); + if (existingTimestamps == null) { + Log.i(TAG, "No extracted resources found"); + return expectedTimestamp; + } + + if (existingTimestamps.length == 1) { + Log.i(TAG, "Found extracted resources " + existingTimestamps[0]); + } + + if (existingTimestamps.length != 1 || !(expectedTimestamp == existingTimestamps[0])) { + Log.i(TAG, "Resource version mismatch " + expectedTimestamp); + return expectedTimestamp; + } + + return null; + } + + private getExistingTimestamps(dataDir: string): string[] { + return fs.accessSync(dataDir) ? fs.listFileSync(dataDir, { + filter: { + displayName: [`${TIMESTAMP_PREFIX}*`] + } + }) : new Array(); + } + + isInitialized(): boolean { + return this.initialized; + } +} + +class InitResult { + appStoragePath: string; + engineCachesPath: string; + dataDirPath: string; + + constructor(appStoragePath: string, + engineCachesPath: string, + dataDirPath: string) { + this.appStoragePath = appStoragePath; + this.engineCachesPath = engineCachesPath; + this.dataDirPath = dataDirPath; + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/mutatorsstack/FlutterMutatorView.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/mutatorsstack/FlutterMutatorView.ets new file mode 100644 index 0000000000000000000000000000000000000000..8d55647eba12d8b76b4b0e4a9b6dbfadbd806eb7 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/mutatorsstack/FlutterMutatorView.ets @@ -0,0 +1,136 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import ArrayList from '@ohos.util.ArrayList'; +import matrix4 from '@ohos.matrix4'; +import { DVModel, DVModelEvents, DVModelParameters } from '../../../view/DynamicView/dynamicView'; +import { createDVModelFromJson } from '../../../view/DynamicView/dynamicViewJson'; +import OhosTouchProcessor from '../../ohos/OhosTouchProcessor'; +import { FlutterMutator, FlutterMutatorsStack } from './FlutterMutatorsStack' +import Any from '../../../plugin/common/Any'; + +export class FlutterMutatorView { + private mutatorsStack: FlutterMutatorsStack | null = null; + private screenDensity: number = 0; + private left: number = 0; + private top: number = 0; + private prevLeft: number = 0; + private prevTop: number = 0; + private onTouch = (touchEvent: Any) => { + let params = this.model.params as Record; + switch (touchEvent.type) { + case TouchType.Down: + this.prevLeft = this.left; + this.prevTop = this.top; + params.translateX = this.left; + params.translateY = this.top; + break; + case TouchType.Move: + params.translateX = this.prevLeft; + params.translateY = this.prevTop; + this.prevLeft = this.left; + this.prevTop = this.top; + break; + case TouchType.Up: + case TouchType.Cancel: + default: + break; + } + } + private model: DVModel = createDVModelFromJson( + new DVModelParam("Column", [], { backgroundColor: Color.Red }, { onTouch: this.onTouch }) + ); + + setOnDescendantFocusChangeListener(onFocus: () => void, onBlur: () => void) { + // this.model.events["onFocus"] = onFocus; + // this.model.events["onBlur"] = onBlur; + let events2 = this.model.events as Record; + events2.onFocus = onFocus; + events2.onBlur = onBlur; + } + + public setLayoutParams(parameters: DVModelParameters): void { + if (this.model.params == null) { + this.model.params = new DVModelParameters(); + } + let params = this.model.params as Record | matrix4.Matrix4Transit>; + let parametersRecord = + parameters as Record | matrix4.Matrix4Transit>; + params.marginLeft = parametersRecord['marginLeft']; + params.marginTop = parametersRecord['marginTop']; + params.width = parametersRecord['width']; + params.height = parametersRecord['height']; + this.left = parametersRecord.marginLeft as number; + this.top = parametersRecord.marginTop as number; + } + + public addDvModel(model: DVModel): void { + this.model?.children.push(model); + } + + public readyToDisplay(mutatorsStack: FlutterMutatorsStack, left: number, top: number, width: number, height: number) { + this.mutatorsStack = mutatorsStack; + this.left = left; + this.top = top; + let parameters = + new DVModelParameters() as Record | matrix4.Matrix4Transit>; + parameters['marginLeft'] = left; + parameters['marginTop'] = top; + parameters['width'] = width; + parameters['height'] = height; + this.setLayoutParams(parameters); + this.dealMutators(); + } + + private dealMutators() { + if (this.mutatorsStack == null) { + return; + } + let paths = this.mutatorsStack.getFinalClippingPaths(); + let rects = this.mutatorsStack.getFinalClippingRects(); + let matrix = this.mutatorsStack.getFinalMatrix(); + let params = this.model.params as Record | matrix4.Matrix4Transit>; + if (!paths.isEmpty()) { + let path = paths.getLast(); + params.pathWidth = path.width; + params.pathHeight = path.height; + params.pathCommands = path.commands; + } + if (!rects.isEmpty()) { + let rect = rects.getLast(); + params.rectWidth = rect.width; + params.rectHeight = rect.height; + params.rectRadius = rect.radius; + } + params.matrix = matrix; + } + + public getDvModel(): DVModel | undefined { + return this.model; + } +} + +class DVModelParam { + compType: string + children: [] + attributes: Any + events: Any + + constructor(compType: string, children: [], attributes: Any, events: Any) { + this.compType = compType; + this.children = children; + this.attributes = attributes; + this.events = events; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/mutatorsstack/FlutterMutatorsStack.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/mutatorsstack/FlutterMutatorsStack.ets new file mode 100644 index 0000000000000000000000000000000000000000..c3804504f1531de952f9ed95b416e6d327f4ab34 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/mutatorsstack/FlutterMutatorsStack.ets @@ -0,0 +1,135 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import matrix4 from '@ohos.matrix4' +import List from '@ohos.util.List'; + +export enum FlutterMutatorType { + CLIP_RECT, + CLIP_PATH, + TRANSFORM, + OPACITY +} + +class Rect { + width: number; + height: number; + radius: string | number | Array; + + constructor(width: number, height: number, radius?: string | number | Array) { + this.width = width; + this.height = height; + this.radius = radius ?? 0; + } +} + +class Path { + width: number | string; + height: number | string; + commands: string; + + constructor(width: number | string, height: number | string, commands?: string) { + this.width = width; + this.height = height; + this.commands = commands ?? ''; + } +} + +export class FlutterMutator { + private matrix: matrix4.Matrix4Transit | null = null; + private rect: Rect = new Rect(0, 0); + private path: Path = new Path(0, 0); + + constructor(args: matrix4.Matrix4Transit | Rect | Path) { + if (args instanceof Rect) { + this.rect = args; + } else if (args instanceof Path) { + this.path = args; + } else { + this.matrix = args; + } + } + + public getMatrix(): matrix4.Matrix4Transit | null { + return this.matrix; + } + + public getRect() { + return this.rect; + } + + public getPath() { + return this.path; + } +} + +export class FlutterMutatorsStack { + private mutators: List; + private finalClippingPaths: List; + private finalClippingRects: List; + private finalMatrix: matrix4.Matrix4Transit; + + constructor() { + this.mutators = new List(); + this.finalClippingPaths = new List(); + this.finalClippingRects = new List(); + this.finalMatrix = matrix4.identity(); + } + + public pushTransform(values: Array): void { + if (values.length != 16) { + return; + } + let index = 0; + let matrix = matrix4.init( + [values[index++], values[index++], values[index++], values[index++], + values[index++], values[index++], values[index++], values[index++], + values[index++], values[index++], values[index++], values[index++], + values[index++], values[index++], values[index++], values[index++]]); + let mutator = new FlutterMutator(matrix); + this.mutators.add(mutator); + this.finalMatrix.combine(matrix); + } + + public pushClipRect(width: number, height: number, radius?: number) { + let rect = new Rect(width, height, radius); + let mutator = new FlutterMutator(rect); + this.mutators.add(mutator); + this.finalClippingRects.add(rect); + } + + public pushClipPath(width: number, height: number, command?: string) { + let path = new Path(width, height, command); + let mutator = new FlutterMutator(path); + this.mutators.add(mutator); + this.finalClippingPaths.add(path); + } + + public getMutators() { + return this.mutators; + } + + public getFinalClippingPaths() { + return this.finalClippingPaths; + } + + public getFinalClippingRects() { + return this.finalClippingRects; + } + + public getFinalMatrix() { + return this.finalMatrix; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/FlutterPlugin.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/FlutterPlugin.ets new file mode 100644 index 0000000000000000000000000000000000000000..e26b708eec6cef66c60ffb705bcecaef303c25be --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/FlutterPlugin.ets @@ -0,0 +1,133 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import common from '@ohos.app.ability.common'; +import { BinaryMessenger } from '../../../plugin/common/BinaryMessenger'; +import PlatformViewFactory from '../../../plugin/platform/PlatformViewFactory'; +import PlatformViewRegistry from '../../../plugin/platform/PlatformViewRegistry'; +import { TextureRegistry } from '../../../view/TextureRegistry'; +import FlutterEngine from '../FlutterEngine'; + +export interface FlutterPlugin { + //获取唯一的类名 类似安卓的ClassRelevant resources that this {@code FlutterPlugin} may need are provided via the {@code + * binding}. The {@code binding} may be cached and referenced until {@link + * #onDetachedFromEngine(FlutterPluginBinding)} is invoked and returns. + */ + onAttachedToEngine(binding: FlutterPluginBinding): void; + + /** + * This {@code FlutterPlugin} has been removed from a {@link + * io.flutter.embedding.engine.FlutterEngine} instance. + * + *

The {@code binding} passed to this method is the same instance that was passed in {@link + * #onAttachedToEngine(FlutterPluginBinding)}. It is provided again in this method as a + * convenience. The {@code binding} may be referenced during the execution of this method, but it + * must not be cached or referenced after this method returns. + * + *

{@code FlutterPlugin}s should release all resources in this method. + */ + onDetachedFromEngine(binding: FlutterPluginBinding): void; +} + +export class FlutterPluginBinding { + private applicationContext: common.Context; + private flutterEngine: FlutterEngine; + private binaryMessenger: BinaryMessenger; + private flutterAssets: FlutterAssets; + private textureRegistry: TextureRegistry; + private platformViewRegistry: PlatformViewRegistry; + + constructor(applicationContext: common.Context, flutterEngine: FlutterEngine, binaryMessenger: BinaryMessenger, + flutterAssets: FlutterAssets, textureRegistry: TextureRegistry, platformViewRegistry?: PlatformViewRegistry) { + this.applicationContext = applicationContext; + this.flutterEngine = flutterEngine; + this.binaryMessenger = binaryMessenger; + this.flutterAssets = flutterAssets; + this.textureRegistry = textureRegistry; + this.platformViewRegistry = platformViewRegistry ?? new EmptyPlatformViewRegistry(); + } + + getApplicationContext(): common.Context { + return this.applicationContext; + } + + getFlutterEngine(): FlutterEngine { + return this.flutterEngine; + } + + getBinaryMessenger(): BinaryMessenger { + return this.binaryMessenger; + } + + getFlutterAssets(): FlutterAssets { + return this.flutterAssets; + } + + getTextureRegistry(): TextureRegistry { + return this.textureRegistry; + } + + public getPlatformViewRegistry(): PlatformViewRegistry { + return this.platformViewRegistry; + } +} + +/** Provides Flutter plugins with access to Flutter asset information. */ +export interface FlutterAssets { + /** + * Returns the relative file path to the Flutter asset with the given name, including the file's + * extension, e.g., {@code "myImage.jpg"}. + * + *

The returned file path is relative to the Ohos app's standard assets directory. + * Therefore, the returned path is appropriate to pass to Ohos's {@code ResourceManage}, but + * the path is not appropriate to load as an absolute path. + */ + getAssetFilePathByName(assetFileName: string): string; + + /** + * Same as {@link #getAssetFilePathByName(String)} but with added support for an explicit + * Ohos {@code bundleName}. + */ + getAssetFilePathByName(assetFileName: string, bundleName: string): string; + + /** + * Returns the relative file path to the Flutter asset with the given subpath, including the + * file's extension, e.g., {@code "/dir1/dir2/myImage.jpg"}. + * + *

The returned file path is relative to the Ohos app's standard assets directory. + * Therefore, the returned path is appropriate to pass to Ohos's {@code ResourceManage}, but + * the path is not appropriate to load as an absolute path. + */ + getAssetFilePathBySubpath(assetSubpath: string): string; + + /** + * Same as {@link #getAssetFilePathBySubpath(String)} but with added support for an explicit + * Ohos {@code bundleName}. + */ + getAssetFilePathBySubpath(assetSubpath: string, bundleName: string): string; +} + +class EmptyPlatformViewRegistry implements PlatformViewRegistry { + registerViewFactory(viewTypeId: string, factory: PlatformViewFactory): boolean { + return false; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/PluginRegistry.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/PluginRegistry.ets new file mode 100644 index 0000000000000000000000000000000000000000..1a322ec2ba1bbcfcbce180b00ec4df6356733180 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/PluginRegistry.ets @@ -0,0 +1,73 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { FlutterPlugin } from './FlutterPlugin'; + +export default interface PluginRegistry { + /** + * Attaches the given {@code plugin} to the {@link io.flutter.embedding.engine.FlutterEngine} + * associated with this {@code PluginRegistry}. + */ + add(plugin: FlutterPlugin): void; + + /** + * Attaches the given {@code plugins} to the {@link io.flutter.embedding.engine.FlutterEngine} + * associated with this {@code PluginRegistry}. + */ + addList(plugins: Set): void; + + /** + * Returns true if a plugin of the given type is currently attached to the {@link + * io.flutter.embedding.engine.FlutterEngine} associated with this {@code PluginRegistry}. + */ + //Class + has(pluginClassName: string): boolean; + + /** + * Returns the instance of a plugin that is currently attached to the {@link + * io.flutter.embedding.engine.FlutterEngine} associated with this {@code PluginRegistry}, which + * matches the given {@code pluginClass}. + * + *

If no matching plugin is found, {@code null} is returned. + */ + //Class + get(pluginClassName: string): FlutterPlugin; + + /** + * Detaches the plugin of the given type from the {@link + * io.flutter.embedding.engine.FlutterEngine} associated with this {@code PluginRegistry}. + * + *

If no such plugin exists, this method does nothing. + */ + //Class + remove(pluginClassName: string): void; + + /** + * Detaches the plugins of the given types from the {@link + * io.flutter.embedding.engine.FlutterEngine} associated with this {@code PluginRegistry}. + * + *

If no such plugins exist, this method does nothing. + */ + //Class + removeList(pluginClassNames: Set): void; + + /** + * Detaches all plugins that are currently attached to the {@link + * io.flutter.embedding.engine.FlutterEngine} associated with this {@code PluginRegistry}. + * + *

If no plugins are currently attached, this method does nothing. + */ + removeAll(): void; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/ability/AbilityAware.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/ability/AbilityAware.ets new file mode 100644 index 0000000000000000000000000000000000000000..373c51552308989de964e037ed02a91460cf366e --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/ability/AbilityAware.ets @@ -0,0 +1,75 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +/** + * {@link io.flutter.embedding.engine.plugins.FlutterPlugin} that is interested in {@link + * ohos.app.ability.UIAbility} lifecycle events related to a {@link + * io.flutter.embedding.engine.FlutterEngine} running within the given {@link ohos.app.ability.UIAbility}. + */ +import { AbilityPluginBinding } from './AbilityPluginBinding'; + +export default interface AbilityAware { + /** + * This {@code AbilityAware} {@link io.flutter.embedding.engine.plugins.FlutterPlugin} is now + * associated with an {@link ohos.app.ability.UIAbility}. + * + *

This method can be invoked in 1 of 2 situations: + * + *

    + *
  • This {@code AbilityAware} {@link io.flutter.embedding.engine.plugins.FlutterPlugin} was + * just added to a {@link io.flutter.embedding.engine.FlutterEngine} that was already + * connected to a running {@link ohos.app.ability.UIAbility}. + *
  • This {@code AbilityAware} {@link io.flutter.embedding.engine.plugins.FlutterPlugin} was + * already added to a {@link io.flutter.embedding.engine.FlutterEngine} and that {@link + * io.flutter.embedding.engine.FlutterEngine} was just connected to an {@link + * ohos.app.ability.UIAbility}. + *
+ * + * The given {@link AbilityPluginBinding} contains {@link ohos.app.ability.UIAbility}-related + * references that an {@code AbilityAware} {@link + * io.flutter.embedding.engine.plugins.FlutterPlugin} may require, such as a reference to the + * actual {@link ohos.app.ability.UIAbility} in question. The {@link AbilityPluginBinding} may be + * referenced until either {@link #onDetachedFromAbilityForConfigChanges()} or {@link + * #onDetachedFromAbility()} is invoked. At the conclusion of either of those methods, the + * binding is no longer valid. Clear any references to the binding or its resources, and do not + * invoke any further methods on the binding or its resources. + */ + onAttachedToAbility(binding: AbilityPluginBinding): void; + + /** + * This plugin has been detached from an {@link ohos.app.ability.UIAbility}. + * + *

Detachment can occur for a number of reasons. + * + *

    + *
  • The app is no longer visible and the {@link ohos.app.ability.UIAbility} instance has been + * destroyed. + *
  • The {@link io.flutter.embedding.engine.FlutterEngine} that this plugin is connected to + * has been detached from its {@link io.flutter.embedding.android.FlutterView}. + *
  • This {@code AbilityAware} plugin has been removed from its {@link + * io.flutter.embedding.engine.FlutterEngine}. + *
+ * + * By the end of this method, the {@link ohos.app.ability.UIAbility} that was made available in {@link + * #onAttachedToAbility(AbilityPluginBinding)} is no longer valid. Any references to the + * associated {@link ohos.app.ability.UIAbility} or {@link AbilityPluginBinding} should be cleared. + * + *

Any {@code Lifecycle} listeners that were registered in {@link + * #onAttachedToAbility(AbilityPluginBinding)} or {@link + * #onReattachedToAbilityForConfigChanges(AbilityPluginBinding)} should be deregistered here to + * avoid a possible memory leak and other side effects. + */ + onDetachedFromAbility(): void; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/ability/AbilityControlSurface.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/ability/AbilityControlSurface.ets new file mode 100644 index 0000000000000000000000000000000000000000..1d7cf7d6cf61551b31579be5b96ada25ebe3028b --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/ability/AbilityControlSurface.ets @@ -0,0 +1,31 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import Want from '@ohos.app.ability.Want'; +import UIAbility from '@ohos.app.ability.UIAbility'; +import ExclusiveAppComponent from '../../../ohos/ExclusiveAppComponent'; + +export default interface ActivityControlSurface { + attachToAbility(exclusiveActivity: ExclusiveAppComponent): void; + + detachFromAbility(): void; + + onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void; + + onWindowFocusChanged(hasFocus: boolean): void; + + onSaveState(reason: AbilityConstant.StateType, wantParam: Record): AbilityConstant.OnSaveResult; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/ability/AbilityPluginBinding.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/ability/AbilityPluginBinding.ets new file mode 100644 index 0000000000000000000000000000000000000000..bc3ede0f800ab66cc422dbad8b343bb7f892aac8 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/plugins/ability/AbilityPluginBinding.ets @@ -0,0 +1,85 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import UIAbility from '@ohos.app.ability.UIAbility' +import Want from '@ohos.app.ability.Want'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; + +export interface AbilityPluginBinding { + getAbility(): UIAbility; + + /** + * Adds a listener that is invoked whenever the associated {@link ohos.app.ability.UIAbility}'s {@code + * onNewWant(...)} method is invoked. + */ + addOnNewWantListener(listener: NewWantListener): void; + + /** + * Removes a listener that was added in {@link + * #addOnNewWantListener(NewWantListener)}. + */ + removeOnNewWantListener(listener: NewWantListener): void; + + /** + * Adds a listener that is invoked whenever the associated {@link ohos.app.ability.UIAbility}'s {@code + * windowStageEvent} method is invoked. + */ + addOnWindowFocusChangedListener(listener: WindowFocusChangedListener): void; + + /** + * Removes a listener that was added in {@link + * #addOnWindowFocusChangedListener(WindowFocusChangedListener)}. + */ + removeOnWindowFocusChangedListener(listener: WindowFocusChangedListener): void; + + /** + * Adds a listener that is invoked when the associated {@code UIAbility} saves + * and restores instance state. + */ + addOnSaveStateListener(listener: OnSaveStateListener): void; + + /** + * Removes a listener that was added in {@link + * #addOnSaveStateListener(OnSaveStateListener)}. + */ + removeOnSaveStateListener(listener: OnSaveStateListener): void; +} + +/** + * Delegate interface for handling new wants on behalf of the main {@link ohos.app.ability.UIAbility}. + */ +export interface NewWantListener { + /** + * @param intent The new want that was started for the UIAbility. + * @return true if the new want has been handled. + */ + onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void; +} + +/** + * Delegate interface for handling window focus changes on behalf of the main {@link + * ohos.app.ability.UIAbility}. + */ +export interface WindowFocusChangedListener { + onWindowFocusChanged(hasFocus: boolean): void; +} + +export interface OnSaveStateListener { + /** + * Invoked when the associated {@code UIAbility} or {@code Fragment} executes {@link + * Activity#onSaveState(Bundle)}. + */ + onSaveState(reason: AbilityConstant.StateType, wantParam: Record): AbilityConstant.OnSaveResult; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/renderer/FlutterRenderer.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/renderer/FlutterRenderer.ets new file mode 100644 index 0000000000000000000000000000000000000000..1c2d106a4425f6c39947b450ff8d58ca39f41835 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/renderer/FlutterRenderer.ets @@ -0,0 +1,119 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import image from '@ohos.multimedia.image'; +import { BusinessError } from '@ohos.base'; +import { SurfaceTextureEntry, TextureRegistry } from '../../../view/TextureRegistry'; +import { FlutterAbility } from '../../ohos/FlutterAbility'; +import FlutterNapi from '../FlutterNapi'; +import Log from '../../../util/Log'; + +const TAG = "FlutterRenderer" + +export class FlutterRenderer implements TextureRegistry { + private flutterNapi: FlutterNapi; + private static globalTextureId: number = 0; + + constructor(flutterNapi: FlutterNapi) { + this.flutterNapi = flutterNapi; + } + + getTextureId(): number { + let nextTextureId: number = FlutterRenderer.globalTextureId + 1; + FlutterRenderer.globalTextureId = FlutterRenderer.globalTextureId + 1; + Log.i(TAG, "getTextureId: " + nextTextureId) + return nextTextureId; + } + + registerTexture(textureId: number): SurfaceTextureEntry { + let surfaceTextureRegistryEntry = new SurfaceTextureRegistryEntry(textureId); + let surfaceId = this.flutterNapi.registerTexture(textureId); + Log.i(TAG, "registerTexture, surfaceId=" + surfaceId); + surfaceTextureRegistryEntry.setSurfaceId(surfaceId); + let nativeWindowId = this.flutterNapi.getTextureNativeWindowId(textureId); + surfaceTextureRegistryEntry.setNativeWindowId(nativeWindowId); + return surfaceTextureRegistryEntry; + } + + + registerPixelMap(pixelMap: PixelMap): number { + let nextTextureId: number = this.getTextureId(); + this.flutterNapi.registerPixelMap(nextTextureId, pixelMap); + return nextTextureId; + } + + setTextureBackGroundPixelMap(textureId: number, pixelMap: PixelMap): void { + this.flutterNapi.setTextureBackGroundPixelMap(textureId, pixelMap); + } + + setTextureBufferSize(textureId: number, width: number, height: number): void { + this.flutterNapi.setTextureBufferSize(textureId, width, height); + } + + notifyTextureResizing(textureId: number, width: number, height: number): void { + this.flutterNapi.notifyTextureResizing(textureId, width, height); + } + + setExternalNativeImage(textureId: number, native_image: number): boolean { + return this.flutterNapi.setExternalNativeImage(textureId, native_image); + } + + resetExternalTexture(textureId: number, need_surfaceId: boolean): number { + return this.flutterNapi.resetExternalTexture(textureId, need_surfaceId); + } + + unregisterTexture(textureId: number): void { + this.flutterNapi.unregisterTexture(textureId); + } + + onTrimMemory(level: number) { + throw new Error('Method not implemented.'); + } +} + +export class SurfaceTextureRegistryEntry implements SurfaceTextureEntry { + private textureId: number = 0; + private surfaceId: number = 0; + private nativeWindowId: number = 0; + private released: boolean = false; + + constructor(id: number) { + this.textureId = id; + } + + getTextureId(): number { + return this.textureId; + } + + getSurfaceId(): number { + return this.surfaceId; + } + + getNativeWindowId(): number { + return this.nativeWindowId; + } + + setSurfaceId(surfaceId: number): void { + this.surfaceId = surfaceId; + } + + setNativeWindowId(nativeWindowId: number): void { + this.nativeWindowId = nativeWindowId; + } + + release() { + throw new Error('Method not implemented.'); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/renderer/FlutterUiDisplayListener.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/renderer/FlutterUiDisplayListener.ets new file mode 100644 index 0000000000000000000000000000000000000000..2a0af4009cafd2cf5229f082a41ed2d445c69caa --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/renderer/FlutterUiDisplayListener.ets @@ -0,0 +1,20 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +export interface FlutterUiDisplayListener { + onFlutterUiDisplayed(): void; + + onFlutterUiNoLongerDisplayed(): void; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/AccessibilityChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/AccessibilityChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..8d60db5830d4c2f1079c07e39267a97ee502c934 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/AccessibilityChannel.ets @@ -0,0 +1,187 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import Log from '../../../util/Log'; +import DartExecutor from '../dart/DartExecutor'; +import BasicMessageChannel, { MessageHandler, Reply } from '../../../plugin/common/BasicMessageChannel'; +import HashMap from '@ohos.util.HashMap'; +import FlutterNapi, { AccessibilityDelegate } from '../FlutterNapi'; +import StandardMessageCodec from '../../../plugin/common/StandardMessageCodec'; +import StringUtils from '../../../util/StringUtils'; +import Any from '../../../plugin/common/Any'; +import flutter from 'libflutter.so'; +import { ByteBuffer } from '../../../util/ByteBuffer'; + +/** + * 辅助功能channel + */ +export default class AccessibilityChannel implements MessageHandler { + private static TAG = "AccessibilityChannel"; + private static CHANNEL_NAME = "flutter/accessibility"; + private channel: BasicMessageChannel; + private flutterNapi: FlutterNapi; + private handler: AccessibilityMessageHandler; + private nextReplyId: number = 1; + + onMessage(message: object, reply: Reply): void { + if (this.handler == null) { + Log.i(AccessibilityChannel.TAG, "handler == NULL"); + reply.reply(StringUtils.stringToArrayBuffer("")); + return; + } + let annotatedEvent: HashMap = message as HashMap; + let type: string = annotatedEvent.get("type") as string; + let data: HashMap = annotatedEvent.get("data") as HashMap; + + Log.i(AccessibilityChannel.TAG, "Received " + type + " message."); + switch (type) { + case "announce": { + Log.i(AccessibilityChannel.TAG, "Announce"); + let announceMessage: string = data.get("message"); + if (announceMessage != null) { + Log.i(AccessibilityChannel.TAG, "message is " + announceMessage); + this.handler.announce(announceMessage); + } + break; + } + case "tap": { + Log.i(AccessibilityChannel.TAG, "Tag"); + let nodeId: number = annotatedEvent.get("nodeId"); + if (nodeId != null) { + this.handler.onTap(nodeId); + } + break; + } + case "longPress": { + Log.i(AccessibilityChannel.TAG, "LongPress"); + let nodeId: number = annotatedEvent.get("nodeId"); + if (nodeId != null) { + this.handler.onLongPress(nodeId); + } + break; + } + case "tooltip": { + Log.i(AccessibilityChannel.TAG, "ToolTip"); + let tooltipMessage: string = data.get("message"); + if (tooltipMessage != null) { + this.handler.onTooltip(tooltipMessage); + } + break; + } + } + reply.reply(StringUtils.stringToArrayBuffer("")); + } + + constructor(dartExecutor: DartExecutor, flutterNapi: FlutterNapi) { + Log.i(AccessibilityChannel.TAG, "Channel entered"); + this.channel = + new BasicMessageChannel(dartExecutor, AccessibilityChannel.CHANNEL_NAME, StandardMessageCodec.INSTANCE); + this.channel.setMessageHandler(this); + this.flutterNapi = flutterNapi; + this.handler = new DefaultHandler(this.flutterNapi); + } + + onOhosAccessibilityEnabled(): void { + let replyId: number = this.nextReplyId++; + this.flutterNapi.setSemanticsEnabledWithRespId(true, replyId); + Log.i(AccessibilityChannel.TAG, "onOhosAccessibilityEnabled = true"); + } + + onOhosAccessibilityFeatures(accessibilityFeatureFlags: number): void { + let replyId: number = this.nextReplyId++; + this.flutterNapi.setAccessibilityFeatures(accessibilityFeatureFlags, replyId); + Log.i(AccessibilityChannel.TAG, "onOhosAccessibilityFeatures"); + } + + dispatchSemanticsAction(virtualViewId: number, action: Action): void { + let replyId: number = this.nextReplyId++; + this.flutterNapi.dispatchSemanticsAction(virtualViewId, action, replyId); + Log.i(AccessibilityChannel.TAG, "dispatchSemanticsAction"); + } + + setAccessibilityMessageHandler(handler: AccessibilityMessageHandler): void { + this.handler = handler; + let replyId: number = this.nextReplyId++; + this.flutterNapi.setAccessibilityDelegate(handler, replyId); + } +} + +export interface AccessibilityMessageHandler extends AccessibilityDelegate { + announce(message: string): void; + + onTap(nodeId: number): void; + + onLongPress(nodeId: number): void; + + onTooltip(nodeId: string): void; +} + +export class DefaultHandler implements AccessibilityMessageHandler { + private static TAG = "AccessibilityMessageHandler"; + private flutterNapi: FlutterNapi; + + constructor(flutterNapi: FlutterNapi) { + this.flutterNapi = flutterNapi; + } + + announce(message: string): void { + Log.i(DefaultHandler.TAG, "handler announce."); + flutter.nativeAccessibilityAnnounce(this.flutterNapi.nativeShellHolderId!, message); + } + + onTap(nodeId: number): void { + Log.i(DefaultHandler.TAG, "handler onTap."); + flutter.nativeAccessibilityOnTap(this.flutterNapi.nativeShellHolderId!, nodeId); + } + + onLongPress(nodeId: number): void { + Log.i(DefaultHandler.TAG, "handler onLongPress."); + flutter.nativeAccessibilityOnLongPress(this.flutterNapi.nativeShellHolderId!, nodeId); + } + + onTooltip(message: string): void { + Log.i(DefaultHandler.TAG, "handler onTooltip."); + flutter.nativeAccessibilityOnTooltip(this.flutterNapi.nativeShellHolderId!, message); + } + + accessibilityStateChange(state: Boolean): void { + Log.i(DefaultHandler.TAG, "handler accessibilityStateChange"); + } +} + +export enum Action { + TAP = 1 << 0, + LONG_PRESS = 1 << 1, + SCROLL_LEFT = 1 << 2, + SCROLL_RIGHT = 1 << 3, + SCROLL_UP = 1 << 4, + SCROLL_DOWN = 1 << 5, + INCREASE = 1 << 6, + DECREASE = 1 << 7, + SHOW_ON_SCREEN = 1 << 8, + MOVE_CURSOR_FORWARD_BY_CHARACTER = 1 << 9, + MOVE_CURSOR_BACKWARD_BY_CHARACTER = 1 << 10, + SET_SELECTION = 1 << 11, + COPY = 1 << 12, + CUT = 1 << 13, + PASTE = 1 << 14, + DID_GAIN_ACCESSIBILITY_FOCUS = 1 << 15, + DID_LOSE_ACCESSIBILITY_FOCUS = 1 << 16, + CUSTOM_ACTION = 1 << 17, + DISMISS = 1 << 18, + MOVE_CURSOR_FORWARD_BY_WORD = 1 << 19, + MOVE_CURSOR_BACKWARD_BY_WORD = 1 << 20, + SET_NEXT = 1 << 21, +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/KeyEventChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/KeyEventChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..b1a375fafdd23cf9912c7e4e0b9ce26489584ba9 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/KeyEventChannel.ets @@ -0,0 +1,73 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import BasicMessageChannel from '../../../plugin/common/BasicMessageChannel'; +import { BinaryMessenger } from '../../../plugin/common/BinaryMessenger'; +import Log from '../../../util/Log'; +import JSONMessageCodec from '../../../plugin/common/JSONMessageCodec'; + +export default class KeyEventChannel { + private static TAG = "KeyEventChannel"; + private static CHANNEL_NAME = "flutter/keyevent"; + private channel: BasicMessageChannel>; + + constructor(binaryMessenger: BinaryMessenger) { + this.channel = new BasicMessageChannel>(binaryMessenger, KeyEventChannel.CHANNEL_NAME, + JSONMessageCodec.INSTANCE); + } + + sendFlutterKeyEvent(keyEvent: FlutterKeyEvent, + isKeyUp: boolean, + responseHandler: EventResponseHandler): void { + this.channel.send(this.encodeKeyEvent(keyEvent, isKeyUp), + (message: Map) => { + let isEventHandled = false; + try { + if (message != null) { + isEventHandled = message.get("handled") as boolean; + } + } catch (e) { + Log.e(KeyEventChannel.TAG, "Unable to unpack JSON message: " + e); + } + responseHandler.onFrameworkResponse(isEventHandled); + } + ); + } + + private encodeKeyEvent(keyEvent: FlutterKeyEvent, isKeyUp: boolean): Map { + let message: Map = new Map(); + message.set("type", isKeyUp ? "keyup" : "keydown"); + message.set("keymap", "ohos"); + message.set("keyCode", keyEvent.event.keyCode); + message.set("deviceId", keyEvent.event.deviceId); + message.set("flags", keyEvent.event.keyText); + message.set("metaState", keyEvent.event.metaKey); + message.set("source", keyEvent.event.keySource); + message.set("intentionCode", keyEvent.event.intentionCode) + return message; + } +} + +export interface EventResponseHandler { + onFrameworkResponse: (isEventHandled: boolean) => void; +} + +export class FlutterKeyEvent { + event: KeyEvent; + + constructor(ohosKeyEvent: KeyEvent) { + this.event = ohosKeyEvent; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/KeyboardChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/KeyboardChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..a9b08a0d4018acdd303bd60122c5b0f6ae4ae247 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/KeyboardChannel.ets @@ -0,0 +1,60 @@ +/* +* Copyright (C) Huawei Technologies Co., Ltd. 2024. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import DartExecutor from '../dart/DartExecutor'; +import MethodChannel, { MethodCallHandler, MethodResult } from '../../../plugin/common/MethodChannel'; +import MethodCall from '../../../plugin/common/MethodCall'; +import StandardMethodCodec from '../../../plugin/common/StandardMethodCodec'; +import Log from '../../../util/Log'; + +export default class KeyboardChannel implements MethodCallHandler { + private static TAG = "KeyboardChannel"; + private static CHANNEL_NAME = "flutter/keyboard"; + private channel: MethodChannel; + private handler: KeyboardMethodHandler | null = null; + + onMethodCall(call: MethodCall, result: MethodResult): void { + if (this.handler == null) { + Log.i(KeyboardChannel.TAG, "KeyboardMethodHandler is null"); + return; + } + + let method: string = call.method; + switch (method) { + case "getKeyboardState": { + Log.i(KeyboardChannel.TAG, "getKeyboardState enter"); + result.success(this.handler?.getKeyboardState()); + break; + } + default: { + result.notImplemented(); + break; + } + } + } + + constructor(dartExecutor: DartExecutor) { + this.channel = new MethodChannel(dartExecutor, KeyboardChannel.CHANNEL_NAME, StandardMethodCodec.INSTANCE); + this.channel.setMethodCallHandler(this); + } + + public setKeyboardMethodHandler(keyboardMessageHandler: KeyboardMethodHandler | null): void { + this.handler = keyboardMessageHandler; + } +} + +export interface KeyboardMethodHandler { + getKeyboardState(): Map; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/LifecycleChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/LifecycleChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..f1fd40451d1b12f9b771aeb68155d9f561536da9 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/LifecycleChannel.ets @@ -0,0 +1,102 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import Log from '../../../util/Log'; +import StringCodec from '../../../plugin/common/StringCodec'; +import DartExecutor from '../dart/DartExecutor'; +import BasicMessageChannel from '../../../plugin/common/BasicMessageChannel'; + +/** + * 生命周期channel + */ +export default class LifecycleChannel { + private static TAG = "LifecycleChannel"; + private static CHANNEL_NAME = "flutter/lifecycle"; + // These should stay in sync with the AppLifecycleState enum in the framework. + private static RESUMED = "AppLifecycleState.resumed"; + private static INACTIVE = "AppLifecycleState.inactive"; + private static PAUSED = "AppLifecycleState.paused"; + private static DETACHED = "AppLifecycleState.detached"; + private lastOhosState = ""; + private lastFlutterState = ""; + private lastFocus = true; + private channel: BasicMessageChannel; + + constructor(dartExecutor: DartExecutor) { + this.channel = new BasicMessageChannel(dartExecutor, LifecycleChannel.CHANNEL_NAME, StringCodec.INSTANCE) + } + + // Called if at least one window in the app has focus. + aWindowIsFocused(): void { + this.sendState(this.lastOhosState, true); + } + + // Called if no windows in the app have focus. + noWindowsAreFocused(): void { + this.sendState(this.lastOhosState, false); + } + + appIsResumed(): void { + this.sendState(LifecycleChannel.RESUMED, this.lastFocus); + } + + appIsInactive(): void { + this.sendState(LifecycleChannel.INACTIVE, this.lastFocus); + } + + appIsPaused(): void { + this.sendState(LifecycleChannel.PAUSED, this.lastFocus); + } + + appIsDetached(): void { + this.sendState(LifecycleChannel.DETACHED, this.lastFocus); + } + + // Here's the state table this implements: + // + // | UIAbility State | Window focused | Flutter state | + // |-----------------|----------------|---------------| + // | onCreate | true | resumed | + // | onCreate | false | inactive | + // | onForeground | true | resumed | + // | onForeground | false | inactive | + // | onBackground | true | paused | + // | onBackground | false | paused | + // | onDestroy | true | detached | + // | onDestroy | false | detached | + + private sendState(state: string, hasFocus: boolean): void { + if (this.lastOhosState == state && hasFocus == this.lastFocus) { + // No inputs changed, so Flutter state could not have changed. + return; + } + let newState: string; + if (state == LifecycleChannel.RESUMED) { + newState = hasFocus ? LifecycleChannel.RESUMED : LifecycleChannel.INACTIVE; + } else { + newState = state; + } + // Keep the last reported values for future updates. + this.lastOhosState = state; + this.lastFocus = hasFocus; + if (newState == this.lastFlutterState) { + // No change in the resulting Flutter state, so don't report anything. + return; + } + Log.i(LifecycleChannel.TAG, "Sending " + newState + " message."); + this.channel.send(newState); + this.lastFlutterState = newState; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/LocalizationChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/LocalizationChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..5bb849f96bc3d7c1406e96625559eac00cd748ec --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/LocalizationChannel.ets @@ -0,0 +1,80 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import DartExecutor from '../dart/DartExecutor'; +import MethodChannel, { MethodCallHandler, MethodResult } from '../../../plugin/common/MethodChannel'; +import MethodCall from '../../../plugin/common/MethodCall'; +import List from '@ohos.util.List'; +import JSONMethodCodec from '../../../plugin/common/JSONMethodCodec'; +import intl from '@ohos.intl'; +import Log from '../../../util/Log'; + +const TAG = "LocalizationChannel"; + +export default class LocalizationChannel implements MethodCallHandler { + private static TAG = "LocalizationChannel"; + private static CHANNEL_NAME = "flutter/localization"; + private channel: MethodChannel; + private localizationMessageHandler: LocalizationMessageHandler | null = null; + + onMethodCall(call: MethodCall, result: MethodResult): void { + if (this.localizationMessageHandler == null) { + Log.e(TAG, "localizationMessageHandler is null"); + return; + } + let method: string = call.method; + switch (method) { + case "Localization.getStringResource": { + Log.i(TAG, "Localization.getStringResource enter"); + let key: string = call.argument("key"); + let localeString: string = ""; + if (call.hasArgument("locale")) { + localeString = call.argument("locale"); + } + result.success(this.localizationMessageHandler?.getStringResource(key, localeString)); + break; + } + default: { + result.notImplemented(); + break; + } + } + } + + constructor(dartExecutor: DartExecutor) { + this.channel = new MethodChannel(dartExecutor, LocalizationChannel.CHANNEL_NAME, JSONMethodCodec.INSTANCE); + this.channel.setMethodCallHandler(this); + } + + setLocalizationMessageHandler(localizationMessageHandler: LocalizationMessageHandler): void { + this.localizationMessageHandler = localizationMessageHandler; + } + + sendLocales(locales: string[]): void { + let data: string[] = []; + for (let i = 0; i < locales.length; i++) { + let locale = new intl.Locale(locales[i]); + data.push(locale.language); + data.push(locale.region); + data.push(locale.script); + data.push(''); // locale.getVariant locale的一种变体 + } + this.channel.invokeMethod("setLocale", data); + } +} + +export interface LocalizationMessageHandler { + getStringResource(key: string, local: string): void; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/MouseCursorChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/MouseCursorChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..dde8696f02c0c5d653cf8c07ff5bc06c2def4393 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/MouseCursorChannel.ets @@ -0,0 +1,84 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import HashMap from '@ohos.util.HashMap'; +import MethodCall from '../../../plugin/common/MethodCall'; +import MethodChannel, { MethodCallHandler, MethodResult } from '../../../plugin/common/MethodChannel'; +import StandardMethodCodec from '../../../plugin/common/StandardMethodCodec'; +import Log from '../../../util/Log'; +import DartExecutor from '../dart/DartExecutor'; + +const TAG: string = 'MouseCursorChannel'; + +export default class MouseCursorChannel implements MethodCallHandler { + public channel: MethodChannel; + private mouseCursorMethodHandler: MouseCursorMethodHandler | null = null; + + onMethodCall(call: MethodCall, result: MethodResult): void { + if (this.mouseCursorMethodHandler === null) { + // if no explicit mouseCursorMethodHandler has been registered then we don't + // need to formed this call to an API. Return + Log.e(TAG, "mouseCursorMethodHandler is null") + return; + } + + let method: string = call.method; + Log.i(TAG, "Received '" + method + "' message."); + try { + // More methods are expected to be added here, hence the switch. + switch (method) { + case "activateSystemCursor": + let argument: HashMap = call.args; + let kind: string = argument.get("kind"); + try { + this.mouseCursorMethodHandler.activateSystemCursor(kind); + } catch (err) { + result.error("error", "Error when setting cursors: " + JSON.stringify(err), null); + break; + } + result.success(true); + break; + default: + break; + } + } catch (error) { + result.error("error", "UnHandled error: " + JSON.stringify(error), null) + } + } + + constructor(dartExecutor: DartExecutor) { + this.channel = new MethodChannel(dartExecutor, "flutter/mousecursor", StandardMethodCodec.INSTANCE); + this.channel.setMethodCallHandler(this); + } + + /** + * Sets the {@link MouseCursorMethodHandler} which receives all events and requests that are + * parsed from the underlying platform channel. + * @param mouseCursorMethodHandler + */ + public setMethodHandler(mouseCursorMethodHandler: MouseCursorMethodHandler | null): void { + this.mouseCursorMethodHandler = mouseCursorMethodHandler; + } + + public synthesizeMethodCall(call: MethodCall, result: MethodResult): void { + this.onMethodCall(call, result); + } +} + +export interface MouseCursorMethodHandler { + // Called when the pointer should start displaying a system mouse cursor + // specified by {@code shapeCode}. + activateSystemCursor(kind: String): void; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/NavigationChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/NavigationChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..f8bf2f822091c14cbf61ccf32514bf008a3f56dc --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/NavigationChannel.ets @@ -0,0 +1,62 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import JSONMethodCodec from '../../../plugin/common/JSONMethodCodec'; +import MethodCall from '../../../plugin/common/MethodCall'; +import MethodChannel, { MethodCallHandler, MethodResult } from '../../../plugin/common/MethodChannel'; +import Log from '../../../util/Log'; +import DartExecutor from '../dart/DartExecutor'; + +export default class NavigationChannel { + private static TAG = "NavigationChannel"; + private channel: MethodChannel; + + constructor(dartExecutor: DartExecutor) { + this.channel = new MethodChannel(dartExecutor, "flutter/navigation", JSONMethodCodec.INSTANCE); + // Provide a default handler that returns an empty response to any messages + // on this channel. + this.channel.setMethodCallHandler(new NavigationCallback()); + } + + setInitialRoute(initialRoute: string): void { + Log.i(NavigationChannel.TAG, "Sending message to set initial route to '" + initialRoute + "'"); + this.channel.invokeMethod("setInitialRoute", initialRoute); + } + + pushRoute(route: string): void { + Log.i(NavigationChannel.TAG, "Sending message to push route '" + route + "'"); + this.channel.invokeMethod("pushRoute", route); + } + + pushRouteInformation(route: string): void { + Log.i(NavigationChannel.TAG, "Sending message to push route information '" + route + "'"); + this.channel.invokeMethod("pushRouteInformation", new Map().set("location", route)); + } + + popRoute(): void { + Log.i(NavigationChannel.TAG, "Sending message to pop route."); + this.channel.invokeMethod("popRoute", null); + } + + setMethodCallHandler(handler: MethodCallHandler) { + this.channel.setMethodCallHandler(handler); + } +} + +class NavigationCallback implements MethodCallHandler { + onMethodCall(call: MethodCall, result: MethodResult) { + result.success(null); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/PlatformChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/PlatformChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..015a62acf9cb71defc223cee6b6387c9c8e353a6 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/PlatformChannel.ets @@ -0,0 +1,486 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import JSONMethodCodec from '../../../plugin/common/JSONMethodCodec'; +import MethodCall from '../../../plugin/common/MethodCall'; +import MethodChannel, { MethodCallHandler, MethodResult } from '../../../plugin/common/MethodChannel'; +import Log from '../../../util/Log'; +import DartExecutor from '../dart/DartExecutor'; +import pasteboard from '@ohos.pasteboard'; +import bundleManager from '@ohos.bundle.bundleManager'; +import window from '@ohos.window'; +import Any from '../../../plugin/common/Any'; +import { BusinessError } from '@kit.BasicServicesKit'; + +export default class PlatformChannel { + private static TAG = "PlatformChannel"; + private static CHANNEL_NAME = "flutter/platform"; + channel: MethodChannel; + platformMessageHandler: PlatformMessageHandler | null = null; + + constructor(dartExecutor: DartExecutor) { + this.channel = new MethodChannel(dartExecutor, PlatformChannel.CHANNEL_NAME, JSONMethodCodec.INSTANCE); + let callback = new PlatformMethodCallback(this); + this.channel.setMethodCallHandler(callback); + } + + setPlatformMessageHandler(platformMessageHandler: PlatformMessageHandler | null): void { + this.platformMessageHandler = platformMessageHandler; + } + + systemChromeChanged(areOverlaysVisible: boolean): void { + Log.d(PlatformChannel.TAG, "Sending 'systemUIChange' message."); + this.channel.invokeMethod("SystemChrome.systemUIChange", [areOverlaysVisible]); + } + + decodeOrientations(encodedOrientations: string[]): number { + let requestedOrientation = 0x00; + let firstRequestedOrientation = 0x00; + for (let index = 0; index < encodedOrientations.length; index += 1) { + let encodedOrientation = encodedOrientations[index]; + Log.d(PlatformChannel.TAG, "encodedOrientation[" + index + "]: " + encodedOrientation); + let orientation = this.getDeviceOrientationFromValue(encodedOrientation); + switch (orientation) { + case DeviceOrientation.PORTRAIT_UP: + requestedOrientation |= 0x01; + break; + case DeviceOrientation.PORTRAIT_DOWN: + requestedOrientation |= 0x04; + break; + case DeviceOrientation.LANDSCAPE_LEFT: + requestedOrientation |= 0x08; + break; + case DeviceOrientation.LANDSCAPE_RIGHT: + requestedOrientation |= 0x02; + break; + } + if (firstRequestedOrientation == 0x00) { + firstRequestedOrientation = requestedOrientation; + } + } + + switch (requestedOrientation) { + case 0x00: + return window.Orientation.UNSPECIFIED; + case 0x01: + return window.Orientation.PORTRAIT; + case 0x02: + return window.Orientation.LANDSCAPE_INVERTED; + case 0x03: + case 0x04: + return window.Orientation.PORTRAIT_INVERTED; + case 0x05: + return window.Orientation.AUTO_ROTATION_PORTRAIT; + case 0x06: + case 0x07: + case 0x08: + return window.Orientation.LANDSCAPE; + case 0x09: + case 0x0a: + return window.Orientation.AUTO_ROTATION_LANDSCAPE; + case 0x0b: + return window.Orientation.LOCKED; + case 0x0c: + case 0x0d: + case 0x0e: + switch (firstRequestedOrientation) { + case 0x01: + return bundleManager.DisplayOrientation.PORTRAIT; + case 0x02: + return bundleManager.DisplayOrientation.LANDSCAPE_INVERTED; + case 0x04: + return bundleManager.DisplayOrientation.PORTRAIT_INVERTED; + case 0x08: + return bundleManager.DisplayOrientation.LANDSCAPE; + } + case 0x0f: + return window.Orientation.AUTO_ROTATION_RESTRICTED; + } + return bundleManager.DisplayOrientation.PORTRAIT; + } + + getFeedbackTypeFromValue(encodedName: string): HapticFeedbackType { + if (encodedName == null) { + return HapticFeedbackType.STANDARD; + } + let feedbackTypes: string[] = [ + HapticFeedbackType.STANDARD, + HapticFeedbackType.LIGHT_IMPACT, + HapticFeedbackType.MEDIUM_IMPACT, + HapticFeedbackType.HEAVY_IMPACT, + HapticFeedbackType.SELECTION_CLICK + ]; + if (feedbackTypes.includes(encodedName as HapticFeedbackType)) { + return encodedName as HapticFeedbackType; + } else { + Log.e(PlatformChannel.TAG, "No such HapticFeedbackType:" + encodedName); + return HapticFeedbackType.STANDARD; + } + } + + getClipboardContentFormatFromValue(encodedName: string): ClipboardContentFormat { + let clipboardFormats: string[] = [ClipboardContentFormat.PLAIN_TEXT]; + if (clipboardFormats.includes(encodedName as ClipboardContentFormat)) { + return encodedName as ClipboardContentFormat; + } + return ClipboardContentFormat.PLAIN_TEXT; + } + + getSystemUiOverlayFromValue(encodedName: string): SystemUiOverlay { + let systemUiOverlays: string[] = [SystemUiOverlay.TOP_OVERLAYS, SystemUiOverlay.BOTTOM_OVERLAYS]; + if (systemUiOverlays.includes(encodedName as SystemUiOverlay)) { + return encodedName as SystemUiOverlay; + } + throw new Error("No such SystemUiOverlay: " + encodedName); + } + + getSystemUiModeFromValue(encodedName: string): SystemUiMode { + let systemUiModes: string[] = [ + SystemUiMode.LEAN_BACK, SystemUiMode.IMMERSIVE, + SystemUiMode.IMMERSIVE_STICKY, SystemUiMode.EDGE_TO_EDGE + ]; + if (systemUiModes.includes(encodedName as SystemUiMode)) { + return encodedName as SystemUiMode; + } + throw new Error("No such SystemUiOverlay: " + encodedName); + } + + getBrightnessFromValue(encodedName: string): Brightness { + let brightnesses: string[] = [Brightness.LIGHT, Brightness.DARK]; + if (brightnesses.includes(encodedName as Brightness)) { + return encodedName as Brightness; + } + throw new Error("No such Brightness: " + encodedName); + } + + getDeviceOrientationFromValue(encodedName: string): DeviceOrientation { + let deviceOrientations: DeviceOrientation[] = [ + DeviceOrientation.PORTRAIT_UP, DeviceOrientation.PORTRAIT_DOWN, + DeviceOrientation.LANDSCAPE_LEFT, DeviceOrientation.LANDSCAPE_RIGHT + ]; + if (deviceOrientations.includes(encodedName as DeviceOrientation)) { + return encodedName as DeviceOrientation; + } + throw new Error("No such DeviceOrientation: " + encodedName); + } +} + +export enum HapticFeedbackType { + STANDARD = "STANDARD", + LIGHT_IMPACT = "HapticFeedbackType.lightImpact", + MEDIUM_IMPACT = "HapticFeedbackType.mediumImpact", + HEAVY_IMPACT = "HapticFeedbackType.heavyImpact", + SELECTION_CLICK = "HapticFeedbackType.selectionClick" +} + +export interface PlatformMessageHandler { + playSystemSound(soundType: SoundType): void; + + vibrateHapticFeedback(feedbackType: HapticFeedbackType): Promise; + + setPreferredOrientations(ohosOrientation: number, result: MethodResult): void; + + setApplicationSwitcherDescription(description: AppSwitcherDescription): void; + + showSystemOverlays(overlays: SystemUiOverlay[]): void; + + showSystemUiMode(mode: SystemUiMode): void; + + setSystemUiChangeListener(): void; + + restoreSystemUiOverlays(): void; + + setSystemUiOverlayStyle(systemUiOverlayStyle: SystemChromeStyle): void; + + popSystemNavigator(): void; + + getClipboardData(result: MethodResult): void; + + setClipboardData(text: string, result: MethodResult): void; + + clipboardHasStrings(): boolean; +} + +export enum ClipboardContentFormat { + PLAIN_TEXT = "text/plain", +} + +export enum SoundType { + CLICK = "SystemSoundType.click", + ALERT = "SystemSoundType.alert", +} + +export class AppSwitcherDescription { + public readonly color: number; + public readonly label: string; + + constructor(color: number, label: string) { + this.color = color; + this.label = label; + } +} + +export enum SystemUiOverlay { + TOP_OVERLAYS = "SystemUiOverlay.top", + BOTTOM_OVERLAYS = "SystemUiOverlay.bottom", +} + +export enum SystemUiMode { + LEAN_BACK = "SystemUiMode.leanBack", + IMMERSIVE = "SystemUiMode.immersive", + IMMERSIVE_STICKY = "SystemUiMode.immersiveSticky", + EDGE_TO_EDGE = "SystemUiMode.edgeToEdge", +} + +export enum Brightness { + LIGHT = "Brightness.light", + DARK = "Brightness.dark", +} + +export class SystemChromeStyle { + public readonly statusBarColor: number | null; + public readonly statusBarIconBrightness: Brightness | null; + public readonly systemStatusBarContrastEnforced: boolean | null; + public readonly systemNavigationBarColor: number | null; + public readonly systemNavigationBarIconBrightness: Brightness | null; + public readonly systemNavigationBarDividerColor: number | null; + public readonly systemNavigationBarContrastEnforced: boolean | null; + + constructor(statusBarColor: number | null, + statusBarIconBrightness: Brightness | null, + systemStatusBarContrastEnforced: boolean | null, + systemNavigationBarColor: number | null, + systemNavigationBarIconBrightness: Brightness | null, + systemNavigationBarDividerColor: number | null, + systemNavigationBarContrastEnforced: boolean | null) { + this.statusBarColor = statusBarColor; + this.statusBarIconBrightness = statusBarIconBrightness; + this.systemStatusBarContrastEnforced = systemStatusBarContrastEnforced; + this.systemNavigationBarColor = systemNavigationBarColor; + this.systemNavigationBarIconBrightness = systemNavigationBarIconBrightness; + this.systemNavigationBarDividerColor = systemNavigationBarDividerColor; + this.systemNavigationBarContrastEnforced = systemNavigationBarContrastEnforced; + } +} + +export enum DeviceOrientation { + PORTRAIT_UP = "DeviceOrientation.portraitUp", + PORTRAIT_DOWN = "DeviceOrientation.portraitDown", + LANDSCAPE_LEFT = "DeviceOrientation.landscapeLeft", + LANDSCAPE_RIGHT = "DeviceOrientation.landscapeRight", +} + +class PlatformMethodCallback implements MethodCallHandler { + private static TAG = "PlatformMethodCallback" + platform: PlatformChannel; + + constructor(platform: PlatformChannel) { + this.platform = platform; + } + + onMethodCall(call: MethodCall, result: MethodResult) { + if (this.platform.platformMessageHandler == null) { + Log.w(PlatformMethodCallback.TAG, "platformMessageHandler is null"); + return; + } + + let method: string = call.method; + let args: Any = call.args; + Log.d(PlatformMethodCallback.TAG, "Received '" + method + "' message."); + try { + switch (method) { + case "SystemSound.play": + break; + case "HapticFeedback.vibrate": + try { + Log.d(PlatformMethodCallback.TAG, "HapticFeedback: " + args as string); + let feedbackType = this.platform.getFeedbackTypeFromValue(args as string); + this.platform.platformMessageHandler.vibrateHapticFeedback(feedbackType) + .then(() => { + result.success(null); + }) + .catch((e: BusinessError) => { + Log.e(PlatformMethodCallback.TAG, `HapticFeedback.vibrate error: ${e.code} - ${e.message}`); + }); + } catch (e) { + Log.e(PlatformMethodCallback.TAG, "HapticFeedback.vibrate error:" + JSON.stringify(e)); + } + break; + case "SystemChrome.setPreferredOrientations": + Log.d(PlatformMethodCallback.TAG, "setPreferredOrientations: " + JSON.stringify(args)); + let ohosOrientation = this.platform.decodeOrientations(args as string[]); + this.platform.platformMessageHandler.setPreferredOrientations(ohosOrientation, result); + break; + case "SystemChrome.setApplicationSwitcherDescription": + Log.d(PlatformMethodCallback.TAG, "setApplicationSwitcherDescription: " + JSON.stringify(args)); + try { + let description: AppSwitcherDescription = this.decodeAppSwitcherDescription(args); + this.platform.platformMessageHandler.setApplicationSwitcherDescription(description); + result.success(null); + } catch (err) { + Log.e(PlatformMethodCallback.TAG, "setApplicationSwitcherDescription err:" + JSON.stringify(err)); + result.error("error", JSON.stringify(err), null); + } + break; + case "SystemChrome.setEnabledSystemUIOverlays": + try { + let overlays: SystemUiOverlay[] = this.decodeSystemUiOverlays(args); + Log.d(PlatformMethodCallback.TAG, "overlays: " + overlays); + this.platform.platformMessageHandler.showSystemOverlays(overlays); + result.success(null); + } catch (err) { + Log.e(PlatformMethodCallback.TAG, "setEnabledSystemUIOverlays err:" + JSON.stringify(err)); + result.error("error", JSON.stringify(err), null); + } + break; + case "SystemChrome.setEnabledSystemUIMode": + try { + Log.d(PlatformMethodCallback.TAG, "setEnabledSystemUIMode args:" + args as string); + let mode: SystemUiMode = this.decodeSystemUiMode(args as string) + this.platform.platformMessageHandler.showSystemUiMode(mode); + } catch (err) { + Log.e(PlatformMethodCallback.TAG, "setEnabledSystemUIMode err:" + JSON.stringify(err)); + result.error("error", JSON.stringify(err), null); + } + break; + case "SystemChrome.setSystemUIChangeListener": + this.platform.platformMessageHandler.setSystemUiChangeListener(); + result.success(null); + break; + case "SystemChrome.restoreSystemUIOverlays": + this.platform.platformMessageHandler.restoreSystemUiOverlays(); + result.success(null); + break; + case "SystemChrome.setSystemUIOverlayStyle": + try { + Log.d(PlatformMethodCallback.TAG, "setSystemUIOverlayStyle asrgs: " + JSON.stringify(args)); + let systemChromeStyle: SystemChromeStyle = this.decodeSystemChromeStyle(args); + this.platform.platformMessageHandler.setSystemUiOverlayStyle(systemChromeStyle); + result.success(null); + } catch (err) { + Log.e(PlatformMethodCallback.TAG, "setSystemUIOverlayStyle err:" + JSON.stringify(err)); + result.error("error", JSON.stringify(err), null); + } + break; + case "SystemNavigator.pop": + this.platform.platformMessageHandler.popSystemNavigator(); + result.success(null); + break; + case "Clipboard.getData": + this.platform.platformMessageHandler.getClipboardData(result); + break; + case "Clipboard.setData": + let clipboardContent: string = args.get('text'); + this.platform.platformMessageHandler.setClipboardData(clipboardContent, result); + break; + case "Clipboard.hasStrings": + let response: Any = new Map().set("value", false); + let systemPasteboard = pasteboard.getSystemPasteboard(); + systemPasteboard.hasData().then((hasData) => { + response.set("value", hasData); + result.success(response); + }).catch((err: Any) => { + Log.e(PlatformMethodCallback.TAG, "systemPasteboard.hasData err: " + JSON.stringify(err)); + }) + break; + default: + result.notImplemented(); + break; + } + } catch (e) { + result.error("error", JSON.stringify(e), null); + } + } + + private decodeAppSwitcherDescription(encodedDescription: Map): AppSwitcherDescription { + let color: number = encodedDescription.get('color') as number; + let label: string = encodedDescription.get('label') as string; + return new AppSwitcherDescription(color, label); + } + + private decodeSystemUiOverlays(encodedSystemUiOverlay: string[]): SystemUiOverlay[] { + let overlays: SystemUiOverlay[] = []; + for (let i = 0; i < encodedSystemUiOverlay.length; i++) { + const encodedOverlay = encodedSystemUiOverlay[i]; + const overlay = this.platform.getSystemUiOverlayFromValue(encodedOverlay); + switch (overlay) { + case SystemUiOverlay.TOP_OVERLAYS: + overlays.push(SystemUiOverlay.TOP_OVERLAYS); + break; + case SystemUiOverlay.BOTTOM_OVERLAYS: + overlays.push(SystemUiOverlay.BOTTOM_OVERLAYS); + break; + } + } + return overlays; + } + + private decodeSystemUiMode(encodedSystemUiMode: string): SystemUiMode { + let mode: SystemUiMode = this.platform.getSystemUiModeFromValue(encodedSystemUiMode); + switch (mode) { + case SystemUiMode.LEAN_BACK: + return SystemUiMode.LEAN_BACK; + case SystemUiMode.IMMERSIVE: + return SystemUiMode.IMMERSIVE; + case SystemUiMode.IMMERSIVE_STICKY: + return SystemUiMode.IMMERSIVE_STICKY; + case SystemUiMode.EDGE_TO_EDGE: + default: + return SystemUiMode.EDGE_TO_EDGE; + } + } + + private decodeSystemChromeStyle(encodedStyle: Map | null): SystemChromeStyle { + let statusBarColor: number | null = null; + let statusBarIconBrightness: Brightness | null = null; + let systemStatusBarContrastEnforced: boolean | null = null; + let systemNavigationBarColor: number | null = null; + let systemNavigationBarIconBrightness: Brightness | null = null; + let systemNavigationBarDividerColor: number | null = null; + let systemNavigationBarContrastEnforced: boolean | null = null; + if (encodedStyle?.get('statusBarColor') != null) { + statusBarColor = encodedStyle.get('statusBarColor') as number; + } + if (encodedStyle?.get('statusBarIconBrightness') != null) { + statusBarIconBrightness = + this.platform.getBrightnessFromValue(encodedStyle.get('statusBarIconBrightness') as string); + } + if (encodedStyle?.get('systemStatusBarContrastEnforced') != null) { + systemStatusBarContrastEnforced = encodedStyle.get('systemStatusBarContrastEnforced') as boolean; + } + if (encodedStyle?.get('systemNavigationBarColor') != null) { + systemNavigationBarColor = encodedStyle.get('systemNavigationBarColor') as number; + } + if (encodedStyle?.get('systemNavigationBarIconBrightness') != null) { + systemNavigationBarIconBrightness = + this.platform.getBrightnessFromValue(encodedStyle.get('systemNavigationBarIconBrightness') as string); + } + if (encodedStyle?.get('systemNavigationBarDividerColor') != null) { + systemNavigationBarDividerColor = encodedStyle.get('systemNavigationBarDividerColor') as number; + } + if (encodedStyle?.get('systemNavigationBarContrastEnforced') != null) { + systemNavigationBarContrastEnforced = encodedStyle.get('systemNavigationBarContrastEnforced') as boolean; + } + return new SystemChromeStyle( + statusBarColor, + statusBarIconBrightness, + systemStatusBarContrastEnforced, + systemNavigationBarColor, + systemNavigationBarIconBrightness, + systemNavigationBarDividerColor, + systemNavigationBarContrastEnforced + ); + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/PlatformViewsChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/PlatformViewsChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..0dc0133ba4750ad5b00c2484526bb82b19366143 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/PlatformViewsChannel.ets @@ -0,0 +1,529 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import Any from '../../../plugin/common/Any'; + +import MethodCall from '../../../plugin/common/MethodCall'; +import MethodChannel, { MethodCallHandler, MethodResult } from '../../../plugin/common/MethodChannel'; +import StandardMethodCodec from '../../../plugin/common/StandardMethodCodec'; +import { ByteBuffer } from '../../../util/ByteBuffer'; +import Log from '../../../util/Log'; +import DartExecutor from '../dart/DartExecutor'; + +const TAG = "PlatformViewsChannel"; +const NON_TEXTURE_FALLBACK = -2; + +export default class PlatformViewsChannel { + private channel: MethodChannel; + private handler: PlatformViewsHandler | null = null; + private parsingHandler = new ParsingCallback(); + + /** + * Constructs a {@code PlatformViewsChannel} that connects HarmonyOS to the Dart code running in + * {@code dartExecutor}. + * + *

The given {@code dartExecutor} is permitted to be idle or executing code. + * + *

See {@link DartExecutor}. + */ + constructor(dartExecutor: DartExecutor) { + this.channel = new MethodChannel(dartExecutor, "flutter/platform_views", StandardMethodCodec.INSTANCE); + this.parsingHandler.platformChannel = this; + this.channel.setMethodCallHandler(this.parsingHandler); + } + + /** + * Sets the {@link PlatformViewsHandler} which receives all events and requests that are parsed + * from the underlying platform views channel. + */ + public setPlatformViewsHandler(handler: PlatformViewsHandler | null): void { + this.handler = handler; + this.parsingHandler.handler = handler; + } + + public invokeViewFocused(viewId: number): void { + if (this.channel == null) { + return; + } + this.channel.invokeMethod("viewFocused", viewId); + } + + create(call: MethodCall, result: MethodResult): void { + const createArgs: Map = call.args; + const usesPlatformViewLayer: boolean = createArgs.has("hybrid") && createArgs.get("hybrid") as boolean; + const additionalParams: ByteBuffer = createArgs.has("params") ? createArgs.get("params") : null; + + let direction: Direction = Direction.Ltr; + if (createArgs.get("direction") == 0) { + direction = Direction.Ltr; + } else if (createArgs.get("direction") == 1) { + direction = Direction.Rtl; + } + + try { + if (usesPlatformViewLayer) { + const request: PlatformViewCreationRequest = new PlatformViewCreationRequest( + createArgs.get("id"), + createArgs.get("viewType"), + 0, + 0, + 0, + 0, + direction, + additionalParams, + RequestedDisplayMode.HYBRID_ONLY + ); + this.handler?.createForPlatformViewLayer(request); + result.success(null); + } else { + const hybridFallback: boolean = createArgs.has("hybridFallback") && createArgs.get("hybridFallback"); + const displayMode: RequestedDisplayMode = + hybridFallback ? RequestedDisplayMode.TEXTURE_WITH_HYBRID_FALLBACK + : RequestedDisplayMode.TEXTURE_WITH_VIRTUAL_FALLBACK; + const request: PlatformViewCreationRequest = new PlatformViewCreationRequest( + createArgs.get("id"), + createArgs.get("viewType"), + createArgs.has("top") ? createArgs.get("top") : 0.0, + createArgs.has("left") ? createArgs.get("left") : 0.0, + createArgs.get("width"), + createArgs.get("height"), + direction, + additionalParams, + displayMode + ); + + Log.i(TAG, `Create texture param id:${request.viewId}, + type:${request.viewType}, + w:${request.logicalWidth}, + h:${request.logicalHeight}, + l:${request.logicalLeft}, + t:${request.logicalTop}, + d:${request.direction}`); + + const textureId = this.handler?.createForTextureLayer(request); + if (textureId == NON_TEXTURE_FALLBACK) { + if (!hybridFallback) { + throw new Error( + "Platform view attempted to fall back to hybrid mode when not requested."); + } + + // A fallback to hybrid mode is indicated with a null texture ID. + result.success(null); + } else { + result.success(textureId); + } + } + } catch (err) { + Log.e(TAG, "create failed" + err); + result.error("error", err, null); + } + } + + dispose(call: MethodCall, result: MethodResult): void { + const disposeArgs: Map = call.args; + const viewId: number = disposeArgs.get("id"); + try { + this.handler?.dispose(viewId); + result.success(null); + } catch (err) { + Log.e(TAG, "dispose failed", err); + result.error("error", err, null); + } + } + + resize(call: MethodCall, result: MethodResult): void { + const resizeArgs: Map = call.args; + const resizeRequest: PlatformViewResizeRequest = new PlatformViewResizeRequest( + resizeArgs.get("id"), + resizeArgs.get("width"), + resizeArgs.get("height") + ); + try { + let resizeCallback = new ResizeCallback(); + resizeCallback.result = result; + this.handler?.resize(resizeRequest, resizeCallback); + } catch (err) { + Log.e(TAG, "resize failed", err); + result.error("error", err, null); + } + } + + offset(call: MethodCall, result: MethodResult): void { + const offsetArgs: Map = call.args; + try { + this.handler?.offset( + offsetArgs.get("id"), + offsetArgs.get("top"), + offsetArgs.get("left")); + result.success(null); + } catch (err) { + Log.e(TAG, "offset failed", err); + result.error("error", err, null); + } + } + + touch(call: MethodCall, result: MethodResult): void { + const args: Array = call.args; + let index = 0; + const touch: PlatformViewTouch = new PlatformViewTouch( + args[index++], + args[index++], + args[index++], + args[index++], + args[index++], + args[index++], + args[index++], + args[index++], + args[index++], + args[index++], + args[index++], + args[index++], + args[index++], + args[index++], + args[index++], + args[index] + ); + + try { + this.handler?.onTouch(touch); + result.success(null); + } catch (err) { + Log.e(TAG, "offset failed", err); + result.error("error", err, null); + } + } + + setDirection(call: MethodCall, result: MethodResult): void { + const setDirectionArgs: Map = call.args; + const newDirectionViewId: number = setDirectionArgs.get("id"); + const direction: number = setDirectionArgs.get("direction"); + + try { + this.handler?.setDirection(newDirectionViewId, direction); + result.success(null); + } catch (err) { + Log.e(TAG, "setDirection failed", err); + result.error("error", err, null); + } + } + + clearFocus(call: MethodCall, result: MethodResult): void { + const viewId: number = call.args; + try { + this.handler?.clearFocus(viewId); + result.success(null); + } catch (err) { + Log.e(TAG, "clearFocus failed", err); + result.error("error", err, null); + } + } + + synchronizeToNativeViewHierarchy(call: MethodCall, result: MethodResult): void { + const yes: boolean = call.args; + try { + this.handler?.synchronizeToNativeViewHierarchy(yes); + result.success(null); + } catch (err) { + Log.e(TAG, "synchronizeToNativeViewHierarchy failed", err); + result.error("error", err, null); + } + } +} + +/** + * Handler that receives platform view messages sent from Flutter to Ohos through a given + * {@link PlatformViewsChannel}. + * + *

To register a {@code PlatformViewsHandler} with a {@link PlatformViewsChannel}, see {@link + * PlatformViewsChannel#setPlatformViewsHandler(PlatformViewsHandler)}. + */ +export interface PlatformViewsHandler { + /* + * The ID returned by {@code createForTextureLayer} to indicate that the requested texture mode + * was not available and the view creation fell back to {@code PlatformViewLayer} mode. + * + * This can only be returned if the {@link PlatformViewCreationRequest} sets + * {@code TEXTURE_WITH_HYBRID_FALLBACK} as the requested display mode. + */ + + /** + * The Flutter application would like to display a new Ohos {@code View}, i.e., platform + * view. + * + *

The Ohos View is added to the view hierarchy. This view is rendered in the Flutter + * framework by a PlatformViewLayer. + * + * @param request The metadata sent from the framework. + */ + createForPlatformViewLayer(request: PlatformViewCreationRequest): void; + + /** + * The Flutter application would like to display a new HarmonyOS {@code View}, i.e., platform + * view. + * + *

The HarmonyOS View is added to the view hierarchy. This view is rendered in the Flutter + * framework by a TextureLayer. + * + * @param request The metadata sent from the framework. + * @return The texture ID. + */ + createForTextureLayer(request: PlatformViewCreationRequest): number; + + /** The Flutter application would like to dispose of an existing HarmonyOS {@code View}. */ + dispose(viewId: number): void; + + /** + * The Flutter application would like to resize an existing HarmonyOS {@code View}. + * + * @param request The request to resize the platform view. + * @param onComplete Once the resize is completed, this is the handler to notify the size of the + * platform view buffer. + */ + resize(request: PlatformViewResizeRequest, onComplete: PlatformViewBufferResized): void; + + /** + * The Flutter application would like to change the offset of an existing HarmonyOS {@code View}. + */ + offset(viewId: number, top: number, left: number): void; + + /** + * The user touched a platform view within Flutter. + * + *

Touch data is reported in {@code touch}. + */ + onTouch(touch: PlatformViewTouch): void; + + /** + * The Flutter application would like to change the layout direction of an existing HarmonyOS + * {@code View}, i.e., platform view. + */ + setDirection(viewId: number, direction: Direction): void; + + /** Clears the focus from the platform view with a give id if it is currently focused. */ + clearFocus(viewId: number): void; + + /** + * Whether the render surface of {@code FlutterView} should be converted to a {@code + * FlutterImageView} when a {@code PlatformView} is added. + * + *

This is done to synchronize the rendering of the PlatformView and the FlutterView. Defaults + * to true. + */ + synchronizeToNativeViewHierarchy(yes: boolean): void; +} + +/** Platform view display modes that can be requested at creation time. */ +enum RequestedDisplayMode { + /** Use Texture Layer if possible, falling back to Virtual Display if not. */ + TEXTURE_WITH_VIRTUAL_FALLBACK, + /** Use Texture Layer if possible, falling back to Hybrid Composition if not. */ + TEXTURE_WITH_HYBRID_FALLBACK, + /** Use Hybrid Composition in all cases. */ + HYBRID_ONLY, +} + +/** Request sent from Flutter to create a new platform view. */ +export class PlatformViewCreationRequest { + /** The ID of the platform view as seen by the Flutter side. */ + public viewId: number; + /** The type of view to create for this platform view. */ + public viewType: string; + /** The density independent width to display the platform view. */ + public logicalWidth: number; + /** The density independent height to display the platform view. */ + public logicalHeight: number; + /** The density independent top position to display the platform view. */ + public logicalTop: number; + /** The density independent left position to display the platform view. */ + public logicalLeft: number; + /** + * The layout direction of the new platform view. + */ + public direction: Direction; + public displayMode: RequestedDisplayMode; + /** Custom parameters that are unique to the desired platform view. */ + public params: ByteBuffer; + + constructor(viewId: number, viewType: string, logicalTop: number, logicalLeft: number, logicalWidth: number, + logicalHeight: number, direction: Direction, params: ByteBuffer, displayMode?: RequestedDisplayMode) { + this.viewId = viewId; + this.viewType = viewType; + this.logicalTop = logicalTop; + this.logicalLeft = logicalLeft; + this.logicalWidth = logicalWidth; + this.logicalHeight = logicalHeight; + this.direction = direction; + this.displayMode = displayMode ? displayMode : RequestedDisplayMode.TEXTURE_WITH_VIRTUAL_FALLBACK; + this.params = params; + } +} + +/** Request sent from Flutter to resize a platform view. */ +export class PlatformViewResizeRequest { + /** The ID of the platform view as seen by the Flutter side. */ + public viewId: number; + /** The new density independent width to display the platform view. */ + public newLogicalWidth: number; + /** The new density independent height to display the platform view. */ + public newLogicalHeight: number; + + constructor(viewId: number, newLogicalWidth: number, newLogicalHeight: number) { + this.viewId = viewId; + this.newLogicalWidth = newLogicalWidth; + this.newLogicalHeight = newLogicalHeight; + } +} + +/** The platform view buffer size. */ +export class PlatformViewBufferSize { + /** The width of the screen buffer. */ + public width: number; + /** The height of the screen buffer. */ + public height: number; + + constructor(width: number, height: number) { + this.width = width; + this.height = height; + } +} + +/** Allows to notify when a platform view buffer has been resized. */ +export abstract class PlatformViewBufferResized { + abstract run(bufferSize: PlatformViewBufferSize): void; +} + +/** The state of a touch event in Flutter within a platform view. */ +export class PlatformViewTouch { + /** The ID of the platform view as seen by the Flutter side. */ + public viewId: number; + /** The amount of time that the touch has been pressed. */ + public downTime: number; + public eventTime: number; + public action: number; + /** The number of pointers (e.g, fingers) involved in the touch event. */ + public pointerCount: number; + /** Properties for each pointer, encoded in a raw format. */ + public rawPointerPropertiesList: Any; + /** Coordinates for each pointer, encoded in a raw format. */ + public rawPointerCoords: Any; + public metaState: number; + public buttonState: number; + /** Coordinate precision along the x-axis. */ + public xPrecision: number; + /** Coordinate precision along the y-axis. */ + public yPrecision: number; + public deviceId: number; + public edgeFlags: number; + public source: number; + public flags: number; + public motionEventId: number; + + constructor(viewId: number, + downTime: number, + eventTime: number, + action: number, + pointerCount: number, + rawPointerPropertiesList: Any, + rawPointerCoords: Any, + metaState: number, + buttonState: number, + xPrecision: number, + yPrecision: number, + deviceId: number, + edgeFlags: number, + source: number, + flags: number, + motionEventId: number) { + this.viewId = viewId; + this.downTime = downTime; + this.eventTime = eventTime; + this.action = action; + this.pointerCount = pointerCount; + this.rawPointerPropertiesList = rawPointerPropertiesList; + this.rawPointerCoords = rawPointerCoords; + this.metaState = metaState; + this.buttonState = buttonState; + this.xPrecision = xPrecision; + this.yPrecision = yPrecision; + this.deviceId = deviceId; + this.edgeFlags = edgeFlags; + this.source = source; + this.flags = flags; + this.motionEventId = motionEventId; + } +} + +class ParsingCallback implements MethodCallHandler { + platformChannel: PlatformViewsChannel | null = null; + handler: PlatformViewsHandler | null = null; + + onMethodCall(call: MethodCall, result: MethodResult) { + if (this.handler == null) { + return; + } + + Log.i(TAG, "Received '" + call.method + "' message."); + switch (call.method) { + case "create": { + this.platformChannel?.create(call, result); + break; + } + case "dispose": { + this.platformChannel?.dispose(call, result); + break; + } + case "resize": { + this.platformChannel?.resize(call, result); + break; + } + case "offset": { + this.platformChannel?.offset(call, result); + break; + } + case "touch": { + this.platformChannel?.touch(call, result); + break; + } + case "setDirection": { + this.platformChannel?.setDirection(call, result); + break; + } + case "clearFocus": { + this.platformChannel?.clearFocus(call, result); + break; + } + case "synchronizeToNativeViewHierarchy": { + this.platformChannel?.synchronizeToNativeViewHierarchy(call, result); + break; + } + default: + result.notImplemented(); + } + } +} + +class ResizeCallback extends PlatformViewBufferResized { + result: MethodResult | null = null; + + run(bufferSize: PlatformViewBufferSize) { + if (bufferSize == null) { + this.result?.error("error", "Failed to resize the platform view", null); + } else { + const response: Map = new Map(); + response.set("width", bufferSize.width); + response.set("height", bufferSize.height); + this.result?.success(response); + } + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/RestorationChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/RestorationChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..58122b3bd1c62b3b41ebb1cff26170ff93dfb563 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/RestorationChannel.ets @@ -0,0 +1,180 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import Any from '../../../plugin/common/Any'; + +import MethodCall from '../../../plugin/common/MethodCall'; +import MethodChannel, { MethodCallHandler, MethodResult } from '../../../plugin/common/MethodChannel'; +import StandardMethodCodec from '../../../plugin/common/StandardMethodCodec'; +import Log from '../../../util/Log'; +import StringUtils from '../../../util/StringUtils'; +import DartExecutor from '../dart/DartExecutor'; + +/** + * System channel to exchange restoration data between framework and engine. + * + *

The engine can obtain the current restoration data from the framework via this channel to + * store it on disk and - when the app is relaunched - provide the stored data back to the framework + * to recreate the original state of the app. + * + *

The channel can be configured to delay responding to the framework's request for restoration + * data via {@code waitForRestorationData} until the engine-side has provided the data. This is + * useful when the engine is pre-warmed at a point in the application's life cycle where the + * restoration data is not available yet. For example, if the engine is pre-warmed as part of the + * Application before an Ability is created, this flag should be set to true because HarmonyOS will + * only provide the restoration data to the Ability during the onCreate callback. + * + *

The current restoration data provided by the framework can be read via {@code + * getRestorationData()}. + */ +export default class RestorationChannel { + private static TAG = "RestorationChannel"; + private static CHANNEL_NAME = "flutter/restoration"; + /** + * Whether the channel delays responding to the framework's initial request for restoration data + * until {@code setRestorationData} has been called. + * + *

If the engine never calls {@code setRestorationData} this flag must be set to false. If set + * to true, the engine must call {@code setRestorationData} either with the actual restoration + * data as argument or null if it turns out that there is no restoration data. + * + *

If the response to the framework's request for restoration data is not delayed until the + * data has been set via {@code setRestorationData}, the framework may intermittently initialize + * itself to default values until the restoration data has been made available. Setting this flag + * to true avoids that extra work. + */ + public waitForRestorationData: boolean = false; + public pendingFrameworkRestorationChannelRequest: MethodResult | null = null; + public engineHasProvidedData: boolean = false; + public frameworkHasRequestedData: boolean = false; + // Holds the most current restoration data which may have been provided by the engine + // via "setRestorationData" or by the framework via the method channel. This is the data the + // framework should be restored to in case the app is terminated. + private restorationData: Uint8Array; + private channel: MethodChannel | null = null; + private handler: MethodCallHandler; + + constructor(channelOrExecutor: MethodChannel | DartExecutor, waitForRestorationData: boolean) { + if (channelOrExecutor instanceof MethodChannel) { + this.channel = channelOrExecutor; + } else { + this.channel = + new MethodChannel(channelOrExecutor, RestorationChannel.CHANNEL_NAME, StandardMethodCodec.INSTANCE); + } + this.waitForRestorationData = waitForRestorationData; + this.restorationData = new Uint8Array(1).fill(0); + this.handler = new RestorationChannelMethodCallHandler(this); + this.channel.setMethodCallHandler(this.handler); + } + + /** Obtain the most current restoration data that the framework has provided. */ + getRestorationData(): Uint8Array { + return this.restorationData; + } + + setRestorationDataOnly(data: Uint8Array) { + this.restorationData = data; + } + + /** Set the restoration data from which the framework will restore its state. */ + setRestorationData(data: Uint8Array) { + this.engineHasProvidedData = true; + if (this.pendingFrameworkRestorationChannelRequest != null) { + // If their is a pending request from the framework, answer it. + this.pendingFrameworkRestorationChannelRequest.success(RestorationChannelMethodCallHandler.packageData(data)); + this.pendingFrameworkRestorationChannelRequest = null; + this.restorationData = data; + } else if (this.frameworkHasRequestedData) { + // If the framework has previously received the engine's restoration data, push the new data + // directly to it. This case can happen when "waitForRestorationData" is false and the + // framework retrieved the restoration state before it was set via this method. + // Experimentally, this can also be used to restore a previously used engine to another state, + // e.g. when the engine is attached to a new activity. + this.channel?.invokeMethod( + "push", RestorationChannelMethodCallHandler.packageData(data), { + success: (result: Any): void => { + this.restorationData = data; + }, + + error: (errorCode: string, errorMessage: string, errorDetails: Any): void => { + Log.e( + RestorationChannel.TAG, + "Error " + errorCode + " while sending restoration data to framework: " + errorMessage + ); + }, + + notImplemented: (): void => { + // do nothing + } + }) + } else { + // Otherwise, just cache the data until the framework asks for it. + this.restorationData = data; + } + } + + /** + * Clears the current restoration data. + * + *

This should be called just prior to a hot restart. Otherwise, after the hot restart the + * state prior to the hot restart will get restored. + */ + clearData() { + this.restorationData = new Uint8Array(1).fill(0); + } +} + +class RestorationChannelMethodCallHandler implements MethodCallHandler { + private channel: RestorationChannel; + + constructor(channel: RestorationChannel) { + this.channel = channel; + } + + onMethodCall(call: MethodCall, result: MethodResult): void { + const method = call.method; + const args: Any = call.args; + switch (method) { + case "put": { + this.channel.setRestorationDataOnly(args); + result.success(null); + break; + } + case "get": { + this.channel.frameworkHasRequestedData = true; + if (this.channel.engineHasProvidedData || !this.channel.waitForRestorationData) { + result.success(RestorationChannelMethodCallHandler.packageData(this.channel.getRestorationData())); + // Do not delete the restoration data on the engine side after sending it to the + // framework. We may need to hand this data back to the operating system if the + // framework never modifies the data (and thus doesn't send us any + // data back). + } else { + this.channel.pendingFrameworkRestorationChannelRequest = result; + } + break; + } + default: { + result.notImplemented(); + break; + } + } + } + + static packageData(data: Uint8Array): Map { + const packaged: Map = new Map(); + packaged.set("enabled", true); + packaged.set("data", data); + return packaged; + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/SettingsChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/SettingsChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..a43f3e240923ebcc7260b2bca7df7192d3a6e6b4 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/SettingsChannel.ets @@ -0,0 +1,100 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import BasicMessageChannel from '../../../plugin/common/BasicMessageChannel'; +import JSONMessageCodec from '../../../plugin/common/JSONMessageCodec'; +import Log from '../../../util/Log'; +import DartExecutor from '../dart/DartExecutor'; + +export enum PlatformBrightness { + LIGHT = "light", + DARK = "dark" +} + +const TAG = "SettingsChannel"; +const TEXT_SCALE_FACTOR = "textScaleFactor"; +const NATIVE_SPELL_CHECK_SERVICE_DEFINED = "nativeSpellCheckServiceDefined"; +const BRIEFLY_SHOW_PASSWORD = "brieflyShowPassword"; +const ALWAYS_USE_24_HOUR_FORMAT = "alwaysUse24HourFormat"; +const PLATFORM_BRIGHTNESS = "platformBrightness"; + +export default class SettingsChannel { + private static CHANNEL_NAME = "flutter/settings"; + private channel: BasicMessageChannel; + + constructor(dartExecutor: DartExecutor) { + this.channel = + new BasicMessageChannel(dartExecutor, SettingsChannel.CHANNEL_NAME, JSONMessageCodec.INSTANCE); + } + + startMessage(): MessageBuilder { + return new MessageBuilder(this.channel); + } +} + +class MessageBuilder { + private channel: BasicMessageChannel; + private settingsMessage: Map = new Map([ + [TEXT_SCALE_FACTOR, 1.0], + [NATIVE_SPELL_CHECK_SERVICE_DEFINED, false], + [BRIEFLY_SHOW_PASSWORD, false], + [ALWAYS_USE_24_HOUR_FORMAT, false], + [PLATFORM_BRIGHTNESS, PlatformBrightness.LIGHT] + ]); + + constructor(channel: BasicMessageChannel) { + this.channel = channel; + } + + setTextScaleFactor(textScaleFactor: Number): MessageBuilder { + this.settingsMessage.set(TEXT_SCALE_FACTOR, textScaleFactor); + return this; + } + + setNativeSpellCheckServiceDefined(nativeSpellCheckServiceDefined: boolean): MessageBuilder { + this.settingsMessage.set(NATIVE_SPELL_CHECK_SERVICE_DEFINED, nativeSpellCheckServiceDefined); + return this; + } + + setBrieflyShowPassword(brieflyShowPassword: boolean): MessageBuilder { + this.settingsMessage.set(BRIEFLY_SHOW_PASSWORD, brieflyShowPassword); + return this; + } + + setAlwaysUse24HourFormat(alwaysUse24HourFormat: boolean): MessageBuilder { + this.settingsMessage.set(ALWAYS_USE_24_HOUR_FORMAT, alwaysUse24HourFormat); + return this; + } + + setPlatformBrightness(platformBrightness: PlatformBrightness): MessageBuilder { + this.settingsMessage.set(PLATFORM_BRIGHTNESS, platformBrightness); + return this; + } + + send(): void { + Log.i(TAG, "Sending message: " + + TEXT_SCALE_FACTOR + " : " + + this.settingsMessage.get(TEXT_SCALE_FACTOR) + + ", " + NATIVE_SPELL_CHECK_SERVICE_DEFINED + " : " + + this.settingsMessage.get(NATIVE_SPELL_CHECK_SERVICE_DEFINED) + + ", " + BRIEFLY_SHOW_PASSWORD + " : " + + this.settingsMessage.get(BRIEFLY_SHOW_PASSWORD) + + ", " + ALWAYS_USE_24_HOUR_FORMAT + " : " + + this.settingsMessage.get(ALWAYS_USE_24_HOUR_FORMAT) + + ", " + PLATFORM_BRIGHTNESS + " : " + + this.settingsMessage.get(PLATFORM_BRIGHTNESS)); + this.channel.send(this.settingsMessage) + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/SystemChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/SystemChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..e3ddc449374eb138f8d03d6f1cbd8d2ffe3b1995 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/SystemChannel.ets @@ -0,0 +1,39 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import BasicMessageChannel from '../../../plugin/common/BasicMessageChannel'; +import Any from '../../../plugin/common/Any'; +import JSONMessageCodec from '../../../plugin/common/JSONMessageCodec'; +import Log from '../../../util/Log'; +import DartExecutor from '../dart/DartExecutor'; + +const TAG: string = "SystemChannel"; + +/** + * fill in javadoc for SystemChannel. + */ +export default class SystemChannel { + public channel: BasicMessageChannel; + + constructor(dartExecutor: DartExecutor) { + this.channel = new BasicMessageChannel(dartExecutor, "flutter/system", JSONMessageCodec.INSTANCE); + } + + public sendMemoryPressureWarning(): void { + Log.i(TAG, "Sending memory pressure warning to Flutter"); + let message: Map = new Map().set("type", "memoryPressure"); + this.channel.send(message); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/TestChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/TestChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..fb6048e61e7e564cb2ccab9b02328252749081b9 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/TestChannel.ets @@ -0,0 +1,38 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import BasicMessageChannel, { MessageHandler, Reply } from '../../../plugin/common/BasicMessageChannel'; +import JSONMessageCodec from '../../../plugin/common/JSONMessageCodec'; +import DartExecutor from '../dart/DartExecutor'; +import Log from '../../../util/Log'; + +const TAG = "TestChannel" + +export default class TestChannel { + private channel: BasicMessageChannel; + + constructor(dartExecutor: DartExecutor) { + this.channel = new BasicMessageChannel(dartExecutor, "flutter/test", JSONMessageCodec.INSTANCE); + let callback = new MessageCallback(); + this.channel.setMessageHandler(callback); + } +} + +class MessageCallback implements MessageHandler { + onMessage(message: string, reply: Reply) { + Log.d(TAG, "receive msg = " + message); + reply.reply("收到消息啦:" + message); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/TextInputChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/TextInputChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..699a1cb3daff849edc4d8715b9ef24db286263f5 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/TextInputChannel.ets @@ -0,0 +1,536 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import JSONMethodCodec from '../../../plugin/common/JSONMethodCodec'; +import MethodCall from '../../../plugin/common/MethodCall'; +import MethodChannel, { MethodCallHandler, MethodResult } from '../../../plugin/common/MethodChannel'; +import TextInputPlugin from '../../../plugin/editing/TextInputPlugin'; +import Log from '../../../util/Log'; +import DartExecutor from '../dart/DartExecutor'; +import inputMethod from '@ohos.inputMethod'; +import ArrayList from '@ohos.util.ArrayList'; +import { TextEditingDelta, TextEditingDeltaJson } from '../../../plugin/editing/TextEditingDelta'; +import Any from '../../../plugin/common/Any'; +import { display } from '@kit.ArkUI' +import { window } from '@kit.ArkUI'; +import { BusinessError, print } from '@kit.BasicServicesKit'; + +const TAG = "TextInputChannel"; +/// 规避换行标识无法显示问题,api修改后再删除 +const NEWLINE_KEY_TYPE: number = 8; + +export default class TextInputChannel { + private static CHANNEL_NAME = "flutter/textinput"; + public channel: MethodChannel; + textInputMethodHandler: TextInputMethodHandler | null = null; + private TextInputCallback: TextInputCallback | null = null; + + constructor(dartExecutor: DartExecutor) { + this.channel = new MethodChannel(dartExecutor, TextInputChannel.CHANNEL_NAME, JSONMethodCodec.INSTANCE); + } + + setTextInputMethodHandler(textInputMethodHandler: TextInputMethodHandler | null): void { + this.textInputMethodHandler = textInputMethodHandler; + this.TextInputCallback = this.textInputMethodHandler == null + ? null : new TextInputCallback(this.textInputMethodHandler); + this.channel.setMethodCallHandler(this.TextInputCallback); + } + + requestExistingInputState(): void { + this.channel.invokeMethod("TextInputClient.requestExistingInputState", null); + } + + createEditingStateJSON(text: string, + selectionStart: number, + selectionEnd: number, + composingStart: number, + composingEnd: number): EditingState { + let state: EditingState = { + text: text, + selectionBase: selectionStart, + selectionExtent: selectionEnd, + composingBase: composingStart, + composingExtent: composingEnd + }; + return state; + } + + createEditingDeltaJSON(batchDeltas: ArrayList): EditingDelta { + let deltas: TextEditingDeltaJson[] = []; + batchDeltas.forEach((val, idx, array) => { + deltas.push(val.toJSON()); + }) + + let state: EditingDelta = { + deltas: deltas, + }; + return state; + } + + /** + * Instructs Flutter to update its text input editing state to reflect the given configuration. + */ + updateEditingState(inputClientId: number, + text: string, + selectionStart: number, + selectionEnd: number, + composingStart: number, + composingEnd: number): void { + Log.d(TAG, "updateEditingState:" + + "Text: " + text + " Selection start: " + selectionStart + " Selection end: " + + selectionEnd + " Composing start: " + composingStart + " Composing end: " + composingEnd); + const state: Any = this.createEditingStateJSON(text, selectionStart, selectionEnd, composingStart, composingEnd); + this.channel.invokeMethod('TextInputClient.updateEditingState', [inputClientId, state]); + } + + updateEditingStateWithDeltas(inputClientId: number, batchDeltas: ArrayList): void { + Log.d(TAG, "updateEditingStateWithDeltas:" + "batchDeltas length: " + batchDeltas.length); + const state: Any = this.createEditingDeltaJSON(batchDeltas); + this.channel.invokeMethod('TextInputClient.updateEditingStateWithDeltas', [inputClientId, state]); + } + + newline(inputClientId: number): void { + Log.d(TAG, "Sending 'newline' message."); + this.channel.invokeMethod("TextInputClient.performAction", [inputClientId, "TextInputAction.newline"]); + } + + go(inputClientId: number): void { + Log.d(TAG, "Sending 'go' message."); + this.channel.invokeMethod("TextInputClient.performAction", [inputClientId, "TextInputAction.go"]); + } + + search(inputClientId: number): void { + Log.d(TAG, "Sending 'search' message."); + this.channel.invokeMethod("TextInputClient.performAction", [inputClientId, "TextInputAction.search"]); + } + + send(inputClientId: number): void { + Log.d(TAG, "Sending 'send' message."); + this.channel.invokeMethod("TextInputClient.performAction", [inputClientId, "TextInputAction.send"]); + } + + done(inputClientId: number): void { + Log.d(TAG, "Sending 'done' message."); + this.channel.invokeMethod("TextInputClient.performAction", [inputClientId, "TextInputAction.done"]); + } + + next(inputClientId: number): void { + Log.d(TAG, "Sending 'next' message."); + this.channel.invokeMethod("TextInputClient.performAction", [inputClientId, "TextInputAction.next"]); + } + + previous(inputClientId: number): void { + Log.d(TAG, "Sending 'previous' message."); + this.channel.invokeMethod("TextInputClient.performAction", [inputClientId, "TextInputAction.previous"]); + } + + unspecifiedAction(inputClientId: number): void { + Log.d(TAG, "Sending 'unspecifiedAction' message."); + this.channel.invokeMethod("TextInputClient.performAction", [inputClientId, "TextInputAction.unspecified"]); + } + + commitContent(inputClientId: number): void { + Log.d(TAG, "Sending 'commitContent' message."); + this.channel.invokeMethod("TextInputClient.performAction", [inputClientId, "TextInputAction.commitContent"]); + } + + onConnectionClosed(inputClientId: number): void { + Log.d(TAG, "Sending 'onConnectionClosed' message."); + this.channel.invokeMethod("TextInputClient.onConnectionClosed", [inputClientId]); + } + + performPrivateCommand(inputClientId: number, action: string, data: Any) { + + } + + public setWindowPosition(windowPosition: window.Rect) { + this.TextInputCallback?.setWindowPosition(windowPosition); + this.TextInputCallback?.setCursorPosition(); + } + + public setDevicePixelRatio(devicePixelRatio: number) { + this.TextInputCallback?.setDevicePixelRatio(devicePixelRatio); + } +} + +interface EditingState { + text: string; + selectionBase: number; + selectionExtent: number; + composingBase: number; + composingExtent: number; +} + + +interface EditingDelta { + deltas: Array; +} + + +export interface TextInputMethodHandler { + show(): void; + + hide(): void; + + requestAutofill(): void; + + finishAutofillContext(shouldSave: boolean): void; + + setClient(textInputClientId: number, configuration: Configuration | null): void; + + setPlatformViewClient(id: number, usesVirtualDisplay: boolean): void; + + setEditableSizeAndTransform(width: number, height: number, transform: number[]): void; + + setCursorSizeAndPosition(cursorInfo: inputMethod.CursorInfo): void; + + setEditingState(editingState: TextEditState): void; + + clearClient(): void; + + handleChangeFocus(focusState: boolean): void; + +} + +export class Configuration { + obscureText: boolean = false; + autocorrect: boolean = false; + autofill: boolean = false; + enableSuggestions: boolean = false; + enableIMEPersonalizedLearning: boolean = false; + enableDeltaModel: boolean = false; + inputType: InputType | null = null; + inputAction: Number = 0; + actionLabel: String = ""; + contentCommitMimeTypes: String[] = []; + fields: Configuration[] = []; + + constructor(obscureText: boolean, + autocorrect: boolean, + enableSuggestions: boolean, + enableIMEPersonalizedLearning: boolean, + enableDeltaModel: boolean, + inputType: InputType, + inputAction: Number, + actionLabel: String, + autofill: boolean, + contentListString: [], + fields: Configuration[] + ) { + this.obscureText = obscureText; + this.autocorrect = autocorrect; + this.enableSuggestions = enableSuggestions; + this.enableIMEPersonalizedLearning = enableIMEPersonalizedLearning; + this.enableDeltaModel = enableDeltaModel; + this.inputType = inputType; + this.inputAction = inputAction; + this.actionLabel = actionLabel; + this.autofill = autofill; + this.contentCommitMimeTypes = contentListString; + this.fields = fields + + } + + private static inputActionFromTextInputAction(inputActionName: string): number { + switch (inputActionName) { + case "TextInputAction.previous": + return inputMethod.EnterKeyType.PREVIOUS + case "TextInputAction.unspecified": + return inputMethod.EnterKeyType.UNSPECIFIED + case "TextInputAction.none": + return inputMethod.EnterKeyType.NONE + case "TextInputAction.go": + return inputMethod.EnterKeyType.GO + case "TextInputAction.search": + return inputMethod.EnterKeyType.SEARCH + case "TextInputAction.send": + return inputMethod.EnterKeyType.SEND + case "TextInputAction.next": + return inputMethod.EnterKeyType.NEXT + case "TextInputAction.newline": + return NEWLINE_KEY_TYPE + case "TextInputAction.done": + return inputMethod.EnterKeyType.DONE + default: + // Present default key if bad input type is given. + return inputMethod.EnterKeyType.UNSPECIFIED + } + } + + static fromJson(json: Any) { + const inputActionName: string = json.inputAction; + if (!inputActionName) { + throw new Error("Configuration JSON missing 'inputAction' property."); + } + + let fields: Array = new Array(); + if (json.fields !== null && json.fields !== undefined) { + fields = json.fields.map((field: Any): Any => Configuration.fromJson(field)); + } + + const inputAction: number = Configuration.inputActionFromTextInputAction(inputActionName); + + // Build list of content commit mime types from the data in the JSON list. + const contentList: Array = []; + if (json.contentCommitMimeTypes !== null && json.contentCommitMimeTypes !== undefined) { + json.contentCommitMimeTypes.forEach((type: Any) => { + contentList.push(type); + }); + } + return new Configuration( + json.obscureText ?? false, + json.autocorrect ?? true, + json.enableSuggestions ?? false, + json.enableIMEPersonalizedLearning ?? false, + json.enableDeltaModel ?? false, + InputType.fromJson(json.inputType), + inputAction, + json.actionLabel ?? null, + json.autofill ?? null, + contentList as Any, + fields + ); + } +} + +/* +/// All possible enum values from flutter. +static const List values = [ + text, multiline, number, phone, datetime, emailAddress, url, visiblePassword, name, streetAddress, none, +]; + +// Corresponding string name for each of the [values]. +static const List _names = [ + 'text', 'multiline', 'number', 'phone', 'datetime', 'emailAddress', 'url', 'visiblePassword', 'name', 'address', 'none', +]; + +// Because TextInputType.name and TextInputType.streetAddress do not exist on ohos, +// these two types will be mapped to the default keyboard. +*/ +const TextInputType: Map = new Map([ + ["TextInputType.text", inputMethod.TextInputType.TEXT], + ["TextInputType.multiline", inputMethod.TextInputType.MULTILINE], + ["TextInputType.number", inputMethod.TextInputType.NUMBER], + ["TextInputType.phone", inputMethod.TextInputType.PHONE], + ["TextInputType.datetime", inputMethod.TextInputType.DATETIME], + ["TextInputType.emailAddress", inputMethod.TextInputType.EMAIL_ADDRESS], + ["TextInputType.url", inputMethod.TextInputType.URL], + ["TextInputType.visiblePassword", inputMethod.TextInputType.VISIBLE_PASSWORD], + ["TextInputType.name", inputMethod.TextInputType.TEXT], + ["TextInputType.address", inputMethod.TextInputType.TEXT], + ["TextInputType.none", inputMethod.TextInputType.NONE], +]); + +export class InputType { + type: inputMethod.TextInputType; + isSigned: boolean; + isDecimal: boolean; + + constructor(type: inputMethod.TextInputType, isSigned: boolean, isDecimal: boolean) { + this.type = type; + this.isSigned = isSigned; + this.isDecimal = isDecimal; + } + + static fromJson(json: Any): InputType { + if (TextInputType.has(json.name as string)) { + return new InputType(TextInputType.get(json.name as string) as inputMethod.TextInputType, + json.signed as boolean, json.decimal as boolean) + } + throw new Error("No such TextInputType: " + json.name as string); + } +} + +export class TextEditState { + private static TAG = "TextEditState"; + text: string; + selectionStart: number; + selectionEnd: number; + composingStart: number; + composingEnd: number; + + constructor(text: string, + selectionStart: number, + selectionEnd: number, + composingStart: number, + composingEnd: number) { + if ((selectionStart != -1 || selectionEnd != -1) + && (selectionStart < 0 || selectionEnd < 0)) { + throw new Error("invalid selection: (" + selectionStart + ", " + selectionEnd + ")"); + } + + if ((composingStart != -1 || composingEnd != -1) + && (composingStart < 0 || composingStart > composingEnd)) { + throw new Error("invalid composing range: (" + composingStart + ", " + composingEnd + ")"); + } + + if (composingEnd > text.length) { + throw new Error("invalid composing start: " + composingStart); + } + + if (selectionStart > text.length) { + throw new Error("invalid selection start: " + selectionStart); + } + + if (selectionEnd > text.length) { + throw new Error("invalid selection end: " + selectionEnd); + } + + this.text = text; + this.selectionStart = selectionStart; + this.selectionEnd = selectionEnd; + this.composingStart = composingStart; + this.composingEnd = composingEnd; + } + + hasSelection(): boolean { + // When selectionStart == -1, it's guaranteed that selectionEnd will also + // be -1. + return this.selectionStart >= 0; + } + + hasComposing(): boolean { + return this.composingStart >= 0 && this.composingEnd > this.composingStart; + } + + static fromJson(textEditState: Any): TextEditState { + if (textEditState.text != null && textEditState.text != undefined && textEditState.text != "") { + return new TextEditState( + textEditState.text, + textEditState.selectionBase, + textEditState.selectionExtent, + textEditState.composingBase, + textEditState.composingExtent + ) + } else { + return new TextEditState( + textEditState.get('text'), + textEditState.get('selectionBase'), + textEditState.get('selectionExtent'), + textEditState.get('composingBase'), + textEditState.get('composingExtent') + ) + } + } +} + +class TextInputCallback implements MethodCallHandler { + textInputMethodHandler: TextInputMethodHandler; + windowPosition: window.Rect | null = null; + cursorPosition: window.Rect = { + left: 0, + top: 0, + width: 0, + height: 0, + } + devicePixelRatio = display.getDefaultDisplaySync()?.densityPixels as number; + inputPosition: window.Rect = { + left: 0, + top: 0, + width: 0, + height: 0, + } + + constructor(handler: TextInputMethodHandler) { + this.textInputMethodHandler = handler; + } + + setWindowPosition(windowPosition: window.Rect) { + this.windowPosition = windowPosition; + } + + setDevicePixelRatio(devicePixelRatio: number) { + this.devicePixelRatio = devicePixelRatio; + } + + setCursorPosition() { + const left = (this.windowPosition?.left ?? 0 as number) + (this.cursorPosition.left + this.inputPosition.left) * this.devicePixelRatio; + const top = (this.windowPosition?.top ?? 0 as number) + (this.cursorPosition.top + this.inputPosition.top) * this.devicePixelRatio; + this.textInputMethodHandler.setCursorSizeAndPosition({ + left: left, + top: top, + width: 100, + height: 50, + }) + } + + + onMethodCall(call: MethodCall, result: MethodResult) { + if (this.textInputMethodHandler == null) { + return; + } + let method: string = call.method; + let args: Any = call.args; + Log.d(TAG, "Received '" + method + "' message."); + switch (method) { + case "TextInput.show": + this.textInputMethodHandler.show(); + Log.d(TAG, "textInputMethodHandler.show()"); + result.success(null); + break; + case "TextInput.hide": + this.textInputMethodHandler.hide(); + result.success(null); + break; + case "TextInput.setClient": + const textInputClientId: number = args[0] as number; + const jsonConfiguration: string = args[1]; + const config: Configuration | null = Configuration.fromJson(jsonConfiguration); + + this.textInputMethodHandler.setClient(textInputClientId, config); + result.success(null); + break; + case "TextInput.requestAutofill": + //TODO: requestAutofill + result.notImplemented(); + break; + case "TextInput.setPlatformViewClient": + //TODO: + result.notImplemented(); + break; + case "TextInput.setEditingState": + this.textInputMethodHandler.setEditingState(TextEditState.fromJson(args)); + result.success(null); + break; + case "TextInput.setCaretRect": + this.cursorPosition.top = args.get('y'); + this.cursorPosition.left = args.get('x'); + this.cursorPosition.width = args.get('width'); + this.cursorPosition.height = args.get('height'); + this.setCursorPosition(); + break; + case "TextInput.setEditableSizeAndTransform": + this.inputPosition.left = args.get('transform')[12]; + this.inputPosition.top = args.get('transform')[13]; + this.setCursorPosition(); + break; + case "TextInput.clearClient": + this.textInputMethodHandler.clearClient(); + result.success(null); + break; + case "TextInput.sendAppPrivateCommand": + //TODO: + result.notImplemented(); + break; + case "TextInput.finishAutofillContext": + //TODO: + result.notImplemented(); + break; + default: + result.notImplemented(); + break; + } + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/workers/PlatformChannelWorker.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/workers/PlatformChannelWorker.ets new file mode 100644 index 0000000000000000000000000000000000000000..bc32d2655da245fd7affcef9392cd937c9d19375 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/workers/PlatformChannelWorker.ets @@ -0,0 +1,74 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS'; + +import Log from '../../../util/Log'; +import SendableBinaryMessageHandler from '../../../plugin/common/SendableBinaryMessageHandler'; +import { TaskState } from '../dart/DartMessenger'; + + +const TAG: string = 'PlatformChannelWorker'; +const workerPort: ThreadWorkerGlobalScope = worker.workerPort; + +/** + * Defines the event handler to be called when the worker thread receives a message sent by the host thread. + * The event handler is executed in the worker thread. + * + * @param e message data + */ +workerPort.onmessage = async (e: MessageEvents) => { + let data: TaskState = e.data; + let result: ArrayBuffer | null = await handleMessage(data.handler, data.message, data.args); + workerPort.postMessage(result, [result]); +} + +/** + * Defines the event handler to be called when the worker receives a message that cannot be deserialized. + * The event handler is executed in the worker thread. + * + * @param e message data + */ +workerPort.onmessageerror = (e: MessageEvents) => { + Log.e(TAG, '#onmessageerror = ' + e.data); +} + +/** + * Defines the event handler to be called when an exception occurs during worker execution. + * The event handler is executed in the worker thread. + * + * @param e error message + */ +workerPort.onerror = (e: ErrorEvent) => { + Log.e(TAG, '#onerror = ' + e.message); +} + +async function handleMessage(handler: SendableBinaryMessageHandler, + message: ArrayBuffer, + args: Object[]): Promise { + const result = await new Promise((resolve, reject) => { + try { + handler.onMessage(message, { + reply: (reply: ArrayBuffer | null): void => { + resolve(reply); + } + }, ...args); + } catch (e) { + reject(null); + Log.e(TAG, "Oops! Failed to handle message in the background: ", e); + } + }); + return result; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/EmbeddingNodeController.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/EmbeddingNodeController.ets new file mode 100644 index 0000000000000000000000000000000000000000..cb2b9db0eaed70347a100d35b58e57dc0be34afd --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/EmbeddingNodeController.ets @@ -0,0 +1,128 @@ +/* +* Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import { BuilderNode, FrameNode, NodeController, NodeRenderType } from '@kit.ArkUI'; +import Any from '../../plugin/common/Any'; +import PlatformView, { Params } from '../../plugin/platform/PlatformView'; +import Log from '../../util/Log'; +import { DVModel, DVModelChildren, DynamicView } from '../../view/DynamicView/dynamicView'; + + +declare class nodeControllerParams { + surfaceId: string + type: string + renderType: NodeRenderType + embedId: string + width: number + height: number +} + +const TAG = 'EmbeddingNodeController' + +export class EmbeddingNodeController extends NodeController { + private builderNode: BuilderNode<[Params]> | undefined | null = null; + private wrappedBuilder: WrappedBuilder<[Params]> | null = null; + private platformView: PlatformView | undefined = undefined; + private embedId: string = ""; + private surfaceId: string = ""; + private renderType: NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY; + private direction: Direction = Direction.Auto; + private isDestroy: boolean = false; + + setRenderOption(platformView: PlatformView, surfaceId: string, renderType: NodeRenderType, direction: Direction) { + if (platformView == undefined) { + Log.e(TAG, "platformView undefined"); + } else { + this.wrappedBuilder = platformView.getView(); + } + this.platformView = platformView; + this.surfaceId = surfaceId; + this.renderType = renderType; + this.direction = direction; + } + + makeNode(uiContext: UIContext): FrameNode | null { + this.builderNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId, type: this.renderType }); + if (this.platformView) { + this.builderNode.build(this.wrappedBuilder, { direction: this.direction, platformView: this.platformView }); + } + return this.builderNode.getFrameNode(); + } + + setBuilderNode(builderNode: BuilderNode | null): void { + this.builderNode = builderNode; + } + + getBuilderNode(): BuilderNode<[Params]> | undefined | null { + return this.builderNode; + } + + updateNode(arg: Object): void { + this.builderNode?.update(arg); + } + + getEmbedId(): string { + return this.embedId; + } + + setDestroy(isDestroy: boolean): void { + this.isDestroy = isDestroy; + if (this.isDestroy) { + this.builderNode?.dispose(); + } + } + + disposeFrameNode() { + this.builderNode?.getFrameNode()?.getRenderNode()?.dispose(); + this.builderNode?.dispose(); + } + + postEvent(event: TouchEvent | undefined, isPx: boolean = false): boolean { + if (event == undefined) { + return false; + } + + // change vp to px + if (!isPx) { + let changedTouchLen = event.changedTouches.length; + for (let i = 0; i < changedTouchLen; i++) { + event.changedTouches[i].displayX = vp2px(event.changedTouches[i].displayX); + event.changedTouches[i].displayY = vp2px(event.changedTouches[i].displayY); + event.changedTouches[i].windowX = vp2px(event.changedTouches[i].windowX); + event.changedTouches[i].windowY = vp2px(event.changedTouches[i].windowY); + event.changedTouches[i].screenX = vp2px(event.changedTouches[i].screenX); + event.changedTouches[i].screenY = vp2px(event.changedTouches[i].screenY); + event.changedTouches[i].x = vp2px(event.changedTouches[i].x); + event.changedTouches[i].y = vp2px(event.changedTouches[i].y); + Log.d(TAG, "changedTouches[" + i + "] displayX:" + event.changedTouches[i].displayX + " displayY:" + + event.changedTouches[i].displayY + " x:" + event.changedTouches[i].x + " y:" + event.changedTouches[i].y); + } + let touchesLen = event.touches.length; + for (let i = 0; i< touchesLen; i++) { + event.touches[i].displayX = vp2px(event.touches[i].displayX); + event.touches[i].displayY = vp2px(event.touches[i].displayY); + event.touches[i].windowX = vp2px(event.touches[i].windowX); + event.touches[i].windowY = vp2px(event.touches[i].windowY); + event.touches[i].screenX = vp2px(event.touches[i].screenX); + event.touches[i].screenY = vp2px(event.touches[i].screenY); + event.touches[i].x = vp2px(event.touches[i].x); + event.touches[i].y = vp2px(event.touches[i].y); + Log.d(TAG, "touches[" + i + "] displayX:" + event.touches[i].displayX + " displayY:" + + event.touches[i].displayY + " x:" + event.touches[i].x + " y:" + event.touches[i].y); + } + } + + return this.builderNode?.postTouchEvent(event) as boolean + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/ExclusiveAppComponent.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/ExclusiveAppComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..f7ecfc7d5ef46c57923679789eb82f86eb83ef77 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/ExclusiveAppComponent.ets @@ -0,0 +1,32 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +export default interface ExclusiveAppComponent { + /** + * Called when another App Component is about to become attached to the {@link + * io.flutter.embedding.engine.FlutterEngine} this App Component is currently attached to. + * + *

This App Component's connections to the {@link io.flutter.embedding.engine.FlutterEngine} + * are still valid at the moment of this call. + */ + detachFromFlutterEngine(): void; + + /** + * Retrieve the App Component behind this exclusive App Component. + * + * @return The app component. + */ + getAppComponent(): T; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbility.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..1b9959be36b3fb77fcf34be6a619aec18601228b --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbility.ets @@ -0,0 +1,378 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import UIAbility from '@ohos.app.ability.UIAbility'; +import window from '@ohos.window'; +import { FlutterAbilityAndEntryDelegate, Host } from './FlutterAbilityAndEntryDelegate'; +import Log from '../../util/Log'; +import FlutterEngine from '../engine/FlutterEngine'; +import PlatformPlugin from '../../plugin/PlatformPlugin'; +import FlutterShellArgs from '../engine/FlutterShellArgs'; +import FlutterAbilityLaunchConfigs from './FlutterAbilityLaunchConfigs'; +import common from '@ohos.app.ability.common'; +import Want from '@ohos.app.ability.Want'; +import { FlutterPlugin } from '../engine/plugins/FlutterPlugin'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import I18n from '@ohos.i18n' +import { PlatformBrightness } from '../engine/systemchannels/SettingsChannel'; +import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant'; +import { Configuration } from '@ohos.app.ability.Configuration'; + +import ExclusiveAppComponent from './ExclusiveAppComponent'; +import errorManager from '@ohos.app.ability.errorManager'; +import appRecovery from '@ohos.app.ability.appRecovery'; +import FlutterManager from './FlutterManager'; +import { FlutterView } from '../../view/FlutterView'; +import ApplicationInfoLoader from '../engine/loader/ApplicationInfoLoader'; +import { accessibility } from '@kit.AccessibilityKit'; + +const TAG = "FlutterAbility"; + +/** + * flutter ohos基础ability,请在让主ability继承自该类。 + * 该类主要职责: + * 1、持有FlutterAbilityDelegate并初始化; + * 2、生命周期传递; + */ +export class FlutterAbility extends UIAbility implements Host { + private delegate?: FlutterAbilityAndEntryDelegate | null; + private flutterView: FlutterView | null = null; + private mainWindow?: window.Window | null; + private errorManagerId: number = 0; + + getFlutterView(): FlutterView | null { + return this.flutterView; + } + + pagePath(): string { + return "pages/Index" + } + + /** + * onCreate + * 1、create and attach delegate + * 2、config windows transparent noNeed? + * 3、lifecycle.onCreate + * 4. setContentView() noNeed + */ + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + // 冷启动通过上下文环境获取到系统当前文字大小,并进行键值存储 + AppStorage.setOrCreate('fontSizeScale', this.context.config.fontSizeScale); + Log.i(TAG, "this.context.config.fontSizeScale = " + this.context.config.fontSizeScale); + + Log.i(TAG, "bundleCodeDir=" + this.context.bundleCodeDir); + FlutterManager.getInstance().pushUIAbility(this) + + this.delegate = new FlutterAbilityAndEntryDelegate(this); + this?.delegate?.onAttach(this.context); + Log.i(TAG, 'onAttach end'); + this?.delegate?.platformPlugin?.setUIAbilityContext(this.context); + this?.delegate?.onRestoreInstanceState(want); + + if (this.stillAttachedForEvent("onWindowStageCreate")) { + this?.delegate?.onWindowStageCreate(); + } + + Log.i(TAG, 'MyAbility onCreate'); + + let observer: errorManager.ErrorObserver = { + onUnhandledException(errorMsg) { + Log.e(TAG, "onUnhandledException, errorMsg:", errorMsg); + appRecovery.saveAppState(); + appRecovery.restartApp(); + } + } + this.errorManagerId = errorManager.on('error', observer); + + let flutterApplicationInfo = ApplicationInfoLoader.load(this.context); + + if (flutterApplicationInfo.isDebugMode) { + this.delegate?.initWindow(); + } + } + + onDestroy() { + FlutterManager.getInstance().popUIAbility(this); + + errorManager.off('error', this.errorManagerId); + + if (this.flutterView != null) { + this.flutterView.onDestroy() + this.flutterView = null; + } + + if (this.stillAttachedForEvent("onDestroy")) { + this?.delegate?.onDetach(); + } + + this.release() + } + + onSaveState(reason: AbilityConstant.StateType, wantParam: Record): AbilityConstant.OnSaveResult { + return this?.delegate?.onSaveState(reason, wantParam) ?? AbilityConstant.OnSaveResult.ALL_REJECT; + } + + protected windowStageEventCallback = (data: window.WindowStageEventType) => { + this.delegate?.onWindowStageChanged(data) + } + + /** + * window状态改变回调 + * @param windowStage + */ + onWindowStageCreate(windowStage: window.WindowStage) { + FlutterManager.getInstance().pushWindowStage(this, windowStage); + this.delegate?.initWindow(); + this.mainWindow = windowStage.getMainWindowSync(); + try { + windowStage.on('windowStageEvent', this.windowStageEventCallback); + this.flutterView = this.delegate!!.createView(this.context) + Log.i(TAG, 'onWindowStageCreate:' + this.flutterView!!.getId()); + let storage: LocalStorage = new LocalStorage(); + storage.setOrCreate("viewId", this.flutterView!!.getId()) + windowStage.loadContent(this.pagePath(), storage, (err, data) => { + if (err.code) { + Log.e(TAG, 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + this.flutterView?.onWindowCreated(); + + Log.i(TAG, 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); + }); + FlutterManager.getInstance().setUseFullScreen(true, this.context); + } catch (exception) { + Log.e(TAG, 'Failed to enable the listener for window stage event changes. Cause:' + JSON.stringify(exception)); + } + } + + onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void { + this?.delegate?.onNewWant(want, launchParams) + } + + onWindowStageDestroy() { + FlutterManager.getInstance().popWindowStage(this); + if (this.stillAttachedForEvent("onWindowStageDestroy")) { + this?.delegate?.onWindowStageDestroy(); + } + } + + onForeground() { + if (this.stillAttachedForEvent("onForeground")) { + this?.delegate?.onShow(); + } + } + + onBackground() { + if (this.stillAttachedForEvent("onBackground")) { + this?.delegate?.onHide(); + } + } + + onWindowStageWillDestroy(windowStage: window.WindowStage) { + try { + windowStage.off('windowStageEvent', this.windowStageEventCallback); + } catch (err) { + Log.e(TAG, "windowStage off failed"); + } + } + + release() { + if (this?.delegate != null) { + this?.delegate?.release(); + this.delegate = null; + } + } + + /** + * host所有实现方法开始======start + */ + + getAbility(): UIAbility { + return this; + } + + getFlutterAbilityAndEntryDelegate(): FlutterAbilityAndEntryDelegate | null { + return this.delegate ?? null; + } + + shouldDispatchAppLifecycleState(): boolean { + return true; + } + + provideFlutterEngine(context: common.Context): FlutterEngine | null { + return null; + } + + providePlatformPlugin(flutterEngine: FlutterEngine): PlatformPlugin | undefined { + return new PlatformPlugin(flutterEngine.getPlatformChannel()!, this.context, this); + } + + configureFlutterEngine(flutterEngine: FlutterEngine) { + + } + + cleanUpFlutterEngine(flutterEngine: FlutterEngine) { + + } + + getFlutterShellArgs(): FlutterShellArgs { + return FlutterShellArgs.fromWant(this.getWant()); + } + + getDartEntrypointArgs(): Array { + if (this.launchWant.parameters![FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT_ARGS]) { + return this.launchWant.parameters![FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT_ARGS] as Array; + } + return new Array() + } + + detachFromFlutterEngine() { + if (this?.delegate != null) { + this?.delegate?.onDetach(); + } + } + + popSystemNavigator(): boolean { + return false; + } + + shouldAttachEngineToAbility(): boolean { + return true; + } + + getDartEntrypointLibraryUri(): string { + return ""; + } + + getAppBundlePath(): string { + return ""; + } + + getDartEntrypointFunctionName(): string { + if (this.launchWant.parameters![FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT]) { + return this.launchWant.parameters![FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT] as string; + } + return FlutterAbilityLaunchConfigs.DEFAULT_DART_ENTRYPOINT + } + + getInitialRoute(): string { + if (this.launchWant.parameters![FlutterAbilityLaunchConfigs.EXTRA_INITIAL_ROUTE]) { + return this.launchWant.parameters![FlutterAbilityLaunchConfigs.EXTRA_INITIAL_ROUTE] as string; + } + return "" + } + + getWant(): Want { + return this.launchWant; + } + + shouldDestroyEngineWithHost(): boolean { + if ((this.getCachedEngineId() != null && this.getCachedEngineId().length > 0) || + this.delegate!!.isFlutterEngineFromHost()) { + // Only destroy a cached engine if explicitly requested by app developer. + return false; + } + return true; + } + + attachToEngineAutomatically(): boolean { + return true; + } + + shouldRestoreAndSaveState(): boolean { + if (this.launchWant.parameters![FlutterAbilityLaunchConfigs.EXTRA_CACHED_ENGINE_ID] != undefined) { + return this.launchWant.parameters![FlutterAbilityLaunchConfigs.EXTRA_CACHED_ENGINE_ID] as boolean; + } + if (this.getCachedEngineId() != null) { + // Prevent overwriting the existing state in a cached engine with restoration state. + return false; + } + return true; + } + + getExclusiveAppComponent(): ExclusiveAppComponent | null { + return this.delegate ? this.delegate : null + } + + getCachedEngineId(): string { + return this.launchWant.parameters![FlutterAbilityLaunchConfigs.EXTRA_CACHED_ENGINE_ID] as string + } + + getCachedEngineGroupId(): string | null { + return this.launchWant.parameters![FlutterAbilityLaunchConfigs.EXTRA_CACHED_ENGINE_GROUP_ID] as string + } + + /** + * host所有实现方法结束======end + */ + private stillAttachedForEvent(event: string) { + Log.i(TAG, 'Ability ' + event); + if (this?.delegate == null) { + Log.w(TAG, "FlutterAbility " + event + " call after release."); + return false; + } + if (!this?.delegate?.isAttached) { + Log.w(TAG, "FlutterAbility " + event + " call after detach."); + return false; + } + return true; + } + + addPlugin(plugin: FlutterPlugin): void { + if (this?.delegate != null) { + this?.delegate?.addPlugin(plugin) + } + } + + removePlugin(plugin: FlutterPlugin): void { + if (this?.delegate != null) { + this?.delegate?.removePlugin(plugin) + } + } + + onMemoryLevel(level: AbilityConstant.MemoryLevel): void { + Log.i(TAG, 'onMemoryLevel: ' + level); + if (level === AbilityConstant.MemoryLevel.MEMORY_LEVEL_CRITICAL) { + this?.delegate?.onLowMemory(); + } + } + + onConfigurationUpdate(config: Configuration) { + Log.i(TAG, 'onConfigurationUpdate config:' + JSON.stringify(config)); + this?.delegate?.flutterEngine?.getSettingsChannel()?.startMessage() + .setNativeSpellCheckServiceDefined(false) + .setBrieflyShowPassword(false) + .setAlwaysUse24HourFormat(I18n.System.is24HourClock()) + .setPlatformBrightness(config.colorMode != ConfigurationConstant.ColorMode.COLOR_MODE_DARK + ? PlatformBrightness.LIGHT : PlatformBrightness.DARK) + .setTextScaleFactor(config.fontSizeScale == undefined ? 1.0 : config.fontSizeScale) + .send(); //热启动生命周期内,实时监听系统设置环境改变并实时发送相应信息 + + //实时获取系统字体加粗系数 + this.delegate?.getFlutterNapi()?.setFontWeightScale(config.fontWeightScale == undefined ? 0 : + config.fontWeightScale); + Log.i(TAG, 'fontWeightScale: ' + JSON.stringify(config.fontWeightScale)); + + if (config.language != '') { + this.getFlutterEngine()?.getLocalizationPlugin()?.sendLocaleToFlutter(); + } + this?.delegate?.onCheckAndReloadFont(); + + } + + getFlutterEngine(): FlutterEngine | null { + return this.delegate?.flutterEngine || null; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbilityAndEntryDelegate.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbilityAndEntryDelegate.ets new file mode 100644 index 0000000000000000000000000000000000000000..70e2817914ef1541361a4f78a8fa4cc29e511168 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbilityAndEntryDelegate.ets @@ -0,0 +1,577 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import common from '@ohos.app.ability.common'; +import FlutterEngineConfigurator from './FlutterEngineConfigurator'; +import FlutterEngineProvider from './FlutterEngineProvider'; +import FlutterEngine from '../engine/FlutterEngine'; +import PlatformPlugin, { PlatformPluginDelegate } from '../../plugin/PlatformPlugin'; +import Want from '@ohos.app.ability.Want'; +import FlutterShellArgs from '../engine/FlutterShellArgs'; +import DartExecutor, { DartEntrypoint } from '../engine/dart/DartExecutor'; +import FlutterAbilityLaunchConfigs from './FlutterAbilityLaunchConfigs'; +import Log from '../../util/Log'; +import FlutterInjector from '../../FlutterInjector'; +import UIAbility from '@ohos.app.ability.UIAbility'; +import ExclusiveAppComponent from './ExclusiveAppComponent'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import { FlutterPlugin } from '../engine/plugins/FlutterPlugin'; +import FlutterEngineCache from '../engine/FlutterEngineCache'; +import FlutterEngineGroupCache from '../engine/FlutterEngineGroupCache'; +import FlutterEngineGroup, { Options } from '../engine/FlutterEngineGroup'; +import FlutterNapi from '../engine/FlutterNapi'; +import { FlutterView } from '../../view/FlutterView'; +import FlutterManager from './FlutterManager'; +import Any from '../../plugin/common/Any'; +import inputMethod from '@ohos.inputMethod'; +import window from '@ohos.window'; + +const TAG = "FlutterAbilityDelegate"; +const PLUGINS_RESTORATION_BUNDLE_KEY = "plugins"; +const FRAMEWORK_RESTORATION_BUNDLE_KEY = "framework"; +const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS'; + +/** + * 主要职责: + * 1、初始化engine + * 2、处理ability生命周期回调 + */ +class FlutterAbilityAndEntryDelegate implements ExclusiveAppComponent { + protected host?: Host | null; + flutterEngine?: FlutterEngine | null; + platformPlugin?: PlatformPlugin; + protected context?: common.Context; + protected isFlutterEngineFromHostOrCache: boolean = false; + private engineGroup?: FlutterEngineGroup; + private isHost: boolean = false; + private flutterView?: FlutterView; + private inputMethodController: inputMethod.InputMethodController = inputMethod.getController(); + + constructor(host?: Host) { + this.host = host; + if (this.host) { + this.isHost = true; + } + } + + /** + * 是否还attach在ability上 + */ + isAttached = false; + + onAttach(context: common.Context) { + this.context = context; + this.ensureAlive(); + if (this.flutterEngine == null) { + this.setupFlutterEngine(); + } + + if (this.host?.shouldAttachEngineToAbility()) { + // Notify any plugins that are currently attached to our FlutterEngine that they + // are now attached to an Ability. + Log.d(TAG, "Attaching FlutterEngine to the Ability that owns this delegate."); + this.flutterEngine?.getAbilityControlSurface()?.attachToAbility(this); + } + + this.platformPlugin = this.host?.providePlatformPlugin(this.flutterEngine!) + + this.isAttached = true; + if (this.flutterEngine) { + this.flutterEngine.getSystemLanguages(); + } + if (this.flutterEngine && this.flutterView && this.host?.attachToEngineAutomatically()) { + this.flutterView.attachToFlutterEngine(this.flutterEngine!!); + } + this.host?.configureFlutterEngine(this.flutterEngine!!); + if (this.flutterEngine) { + this.flutterEngine.processPendingMessages(); + } + this.context.eventHub.on(EVENT_BACK_PRESS, () => { + if (this.flutterView?.getKeyboardHeight() == 0) { + this.flutterEngine?.getNavigationChannel()?.popRoute(); + } else { + this.flutterEngine?.getTextInputChannel()?.textInputMethodHandler?.hide(); + } + }); + } + + /** + * 加载app.so资源或者snapshot + */ + private doInitialFlutterViewRun(): void { + let initialRoute = this.host?.getInitialRoute(); + if (initialRoute == null && this.host != null) { + initialRoute = this.maybeGetInitialRouteFromIntent(this.host.getWant()); + + } + if (initialRoute == null) { + initialRoute = FlutterAbilityLaunchConfigs.DEFAULT_INITIAL_ROUTE; + } + const libraryUri = this.host?.getDartEntrypointLibraryUri(); + Log.d(TAG, + "Executing Dart entrypoint: " + this.host?.getDartEntrypointFunctionName() + ", library uri: " + libraryUri == + null ? "\"\"" : libraryUri + ", and sending initial route: " + initialRoute); + + // The engine needs to receive the Flutter app's initial route before executing any + // Dart code to ensure that the initial route arrives in time to be applied. + this.flutterEngine?.getNavigationChannel()?.setInitialRoute(initialRoute ?? ''); + + let appBundlePathOverride = this.host?.getAppBundlePath(); + if (appBundlePathOverride == null || appBundlePathOverride == '') { + appBundlePathOverride = FlutterInjector.getInstance().getFlutterLoader().findAppBundlePath(); + } + + const dartEntrypoint: DartEntrypoint = new DartEntrypoint( + appBundlePathOverride, + this.host?.getDartEntrypointLibraryUri() ?? '', + this.host?.getDartEntrypointFunctionName() ?? '' + ); + this.flutterEngine?.dartExecutor.executeDartEntrypoint(dartEntrypoint, this.host?.getDartEntrypointArgs()); + } + + private maybeGetInitialRouteFromIntent(want: Want): string { + return ''; + } + + /** + * 通过参数,配置flutterEngine + * @param want + */ + onRestoreInstanceState(want: Want) { + let frameworkState: Uint8Array = this.getRestorationData(want.parameters as Record); + if (this.host?.shouldRestoreAndSaveState()) { + this.flutterEngine?.getRestorationChannel()?.setRestorationData(frameworkState); + } + } + + private getRestorationData(wantParam: Record): Uint8Array { + let result: Uint8Array = new Uint8Array(1).fill(0); + if (wantParam == null) { + return result; + } + if (wantParam[FRAMEWORK_RESTORATION_BUNDLE_KEY] == undefined) { + return result + } + if (typeof wantParam[FRAMEWORK_RESTORATION_BUNDLE_KEY] == 'object') { + let data: Record = wantParam[FRAMEWORK_RESTORATION_BUNDLE_KEY] as Record; + let byteArray: Array = new Array; + Object.keys(data).forEach( + key => { + byteArray.push(data[key]); + } + ); + result = Uint8Array.from(byteArray); + } + return result; + } + + /** + * 初始化flutterEngine + */ + setupFlutterEngine() { + // First, check if the host wants to use a cached FlutterEngine. + const cachedEngineId = this.host?.getCachedEngineId(); + Log.d(TAG, "cachedEngineId=" + cachedEngineId); + if (cachedEngineId && cachedEngineId.length > 0) { + this.flutterEngine = FlutterEngineCache.getInstance().get(cachedEngineId); + this.isFlutterEngineFromHostOrCache = true; + if (this.flutterEngine == null) { + throw new Error( + "The requested cached FlutterEngine did not exist in the FlutterEngineCache: '" + + cachedEngineId + + "'"); + } + return; + } + + // Second, defer to subclasses for a custom FlutterEngine. + if (this.host && this.context) { + this.flutterEngine = this.host.provideFlutterEngine(this.context); + } + if (this.flutterEngine != null) { + this.isFlutterEngineFromHostOrCache = true; + return; + } + + // Third, check if the host wants to use a cached FlutterEngineGroup + // and create new FlutterEngine using FlutterEngineGroup#createAndRunEngine + const cachedEngineGroupId = this.host?.getCachedEngineGroupId(); + Log.d(TAG, "cachedEngineGroupId=" + cachedEngineGroupId); + if (cachedEngineGroupId != null) { + const flutterEngineGroup = FlutterEngineGroupCache.instance.get(cachedEngineGroupId); + if (flutterEngineGroup == null) { + throw new Error( + "The requested cached FlutterEngineGroup did not exist in the FlutterEngineGroupCache: '" + + cachedEngineGroupId + + "'"); + } + + if (this.context != null) { + this.flutterEngine = flutterEngineGroup.createAndRunEngineByOptions( + this.addEntrypointOptions(new Options(this.context))); + } + this.isFlutterEngineFromHostOrCache = false; + return; + } + + // Our host did not provide a custom FlutterEngine. Create a FlutterEngine to back our + // FlutterView. + Log.d( + TAG, + "No preferred FlutterEngine was provided. Creating a new FlutterEngine for this FlutterAbility."); + + let group = this.engineGroup; + if (group == null && this.context != null) { + group = new FlutterEngineGroup(); + const flutterShellArgs = this.host ? this.host.getFlutterShellArgs() : new FlutterShellArgs(); + group.checkLoader(this.context, flutterShellArgs.toArray() ?? []); + this.engineGroup = group; + } + if (this.context) { + this.flutterEngine = group?.createAndRunEngineByOptions(this.addEntrypointOptions(new Options(this.context) + .setWaitForRestorationData(this.host?.shouldRestoreAndSaveState() || false))); + } + this.isFlutterEngineFromHostOrCache = false; + } + + addEntrypointOptions(options: Options): Options { + let appBundlePathOverride = this.host?.getAppBundlePath(); + if (appBundlePathOverride == null || appBundlePathOverride.length == 0) { + appBundlePathOverride = FlutterInjector.getInstance().getFlutterLoader().findAppBundlePath(); + } + + const dartEntrypoint = new DartEntrypoint(appBundlePathOverride ?? '', + '', + this.host?.getDartEntrypointFunctionName() ?? ''); + let initialRoute = this.host?.getInitialRoute(); + if (initialRoute == null && this.host != null) { + initialRoute = this.maybeGetInitialRouteFromIntent(this.host.getWant()); + } + if (initialRoute == null) { + initialRoute = FlutterAbilityLaunchConfigs.DEFAULT_INITIAL_ROUTE; + } + return options + .setDartEntrypoint(dartEntrypoint) + .setInitialRoute(initialRoute) + .setDartEntrypointArgs(this.host?.getDartEntrypointArgs() ?? []); + } + + createView(context: Context): FlutterView { + this.flutterView = FlutterManager.getInstance().createFlutterView(context) + if (this.flutterEngine && this.host?.attachToEngineAutomatically()) { + this.flutterView.attachToFlutterEngine(this.flutterEngine!!); + } + return this.flutterView + } + + /** + * 释放所有持有对象 + */ + release() { + this.host = null; + this.flutterEngine = null; + this.platformPlugin = undefined; + } + + onDetach() { + if (this.host?.shouldAttachEngineToAbility()) { + // Notify plugins that they are no longer attached to an Ability. + Log.d(TAG, "Detaching FlutterEngine from the Ability"); + this.flutterEngine?.getAbilityControlSurface()?.detachFromAbility(); + } + this.flutterView?.detachFromFlutterEngine(); + this.host?.cleanUpFlutterEngine(this.flutterEngine!!); + + if (this.host?.shouldDispatchAppLifecycleState() && this.flutterEngine != null) { + this.flutterEngine?.getLifecycleChannel()?.appIsDetached(); + } + + if (this.platformPlugin) { + this.platformPlugin.destroy(); + } + + // Destroy our FlutterEngine if we're not set to retain it. + if (this.host?.shouldDestroyEngineWithHost()) { + this.flutterEngine?.destroy(); + if (this.host.getCachedEngineId() != null && this.host.getCachedEngineId().length > 0) { + FlutterEngineCache.getInstance().remove(this.host.getCachedEngineId()); + } + this.flutterEngine = null; + } + + this.isAttached = false; + this.context?.eventHub.off(EVENT_BACK_PRESS); + } + + onLowMemory(): void { + this.getFlutterNapi()?.notifyLowMemoryWarning(); + this.flutterEngine?.getSystemChannel()?.sendMemoryPressureWarning(); + } + + /** + * 生命周期回调 + */ + + onWindowStageCreate() { + this.ensureAlive(); + this.doInitialFlutterViewRun(); + } + + onWindowStageDestroy() { + + } + + onWindowStageChanged(stageEventType: window.WindowStageEventType) { + switch (stageEventType) { + case window.WindowStageEventType.SHOWN: + Log.i(TAG, 'windowStage shown.'); + break; + case window.WindowStageEventType.ACTIVE: // 获焦状态 + Log.i(TAG, 'windowStage active.'); + this.getFlutterEngine()?.getTextInputChannel()?.textInputMethodHandler?.handleChangeFocus(true); + this.onWindowFocusChanged(true); + break; + case window.WindowStageEventType.INACTIVE: // 失焦状态 + Log.i(TAG, 'windowStage inactive.'); + this.onWindowFocusChanged(false); + break; + case window.WindowStageEventType.PAUSED: + Log.i(TAG, 'windowStage paused.'); + this.onPaused(); + break; + case window.WindowStageEventType.RESUMED: + Log.i(TAG, 'windowStage resumed.'); + this.onResumed(); + break; + case window.WindowStageEventType.HIDDEN: + Log.i(TAG, 'windowStage hidden.'); + break; + } + } + + onWindowFocusChanged(hasFocus: boolean): void { + if (this.shouldDispatchAppLifecycleState()) { + this.flutterEngine?.getAbilityControlSurface()?.onWindowFocusChanged(hasFocus); + if (hasFocus) { + this.flutterEngine?.getLifecycleChannel()?.aWindowIsFocused(); + } else { + this.flutterEngine?.getLifecycleChannel()?.noWindowsAreFocused(); + } + } + } + + onShow() { + this.ensureAlive(); + if (this.shouldDispatchAppLifecycleState()) { + this.flutterEngine?.getLifecycleChannel()?.appIsResumed(); + } + } + + onPaused() { + if (this.shouldDispatchAppLifecycleState()) { + this.flutterEngine?.getLifecycleChannel()?.appIsInactive(); + } + } + + onResumed() { + if (this.shouldDispatchAppLifecycleState()) { + this.flutterEngine?.getLifecycleChannel()?.appIsResumed(); + } + } + + onHide() { + if (this.shouldDispatchAppLifecycleState()) { + this.flutterEngine?.getLifecycleChannel()?.appIsPaused(); + } + } + + onCheckAndReloadFont() { + this.getFlutterNapi()?.checkAndReloadFont(); + } + + /** + * 生命周期回调结束 + */ + + shouldDispatchAppLifecycleState(): boolean { + if (!this.isHost) { + return this.isAttached; + } + if (this.host == null) { + return false; + } + return this.host.shouldDispatchAppLifecycleState() && this.isAttached; + } + + ensureAlive() { + if (this.isHost && this.host == null) { + throw new Error("Cannot execute method on a destroyed FlutterAbilityDelegate."); + } + } + + getFlutterNapi(): FlutterNapi | null { + return this.flutterEngine?.getFlutterNapi() ?? null + } + + getFlutterEngine(): FlutterEngine | null { + return this.flutterEngine ?? null; + } + + detachFromFlutterEngine() { + if (this.host?.shouldDestroyEngineWithHost()) { + // The host owns the engine and should never have its engine taken by another exclusive + // ability. + throw new Error( + "The internal FlutterEngine created by " + + this.host + + " has been attached to by another Ability. To persist a FlutterEngine beyond the " + + "ownership of this ability, explicitly create a FlutterEngine"); + } + + // Default, but customizable, behavior is for the host to call {@link #onDetach} + // deterministically as to not mix more events during the lifecycle of the next exclusive + // ability. + this.host?.detachFromFlutterEngine(); + } + + getAppComponent(): UIAbility { + const ability = this.host?.getAbility(); + if (ability == null) { + throw new Error( + "FlutterAbilityAndFragmentDelegate's getAppComponent should only " + + "be queried after onAttach, when the host's ability should always be non-null"); + } + return ability; + } + + onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void { + this.ensureAlive() + if (this.flutterEngine != null) { + Log.i(TAG, "Forwarding onNewWant() to FlutterEngine and sending pushRouteInformation message."); + this.flutterEngine?.getAbilityControlSurface()?.onNewWant(want, launchParams); + const initialRoute = this.maybeGetInitialRouteFromIntent(want); + if (initialRoute && initialRoute.length > 0) { + this.flutterEngine?.getNavigationChannel()?.pushRouteInformation(initialRoute); + } + } else { + Log.w(TAG, "onNewIntent() invoked before FlutterFragment was attached to an Ability."); + } + } + + onSaveState(reason: AbilityConstant.StateType, wantParam: Record): AbilityConstant.OnSaveResult { + Log.i(TAG, "onSaveInstanceState. Giving framework and plugins an opportunity to save state."); + this.ensureAlive(); + if (this.host?.shouldRestoreAndSaveState()) { + wantParam[FRAMEWORK_RESTORATION_BUNDLE_KEY] = this.flutterEngine!.getRestorationChannel()!.getRestorationData(); + } + if (this.host?.shouldAttachEngineToAbility()) { + const plugins: Record = {} + const result = this.flutterEngine?.getAbilityControlSurface()?.onSaveState(reason, plugins); + wantParam[PLUGINS_RESTORATION_BUNDLE_KEY] = plugins; + return result ?? AbilityConstant.OnSaveResult.ALL_REJECT + } + return AbilityConstant.OnSaveResult.ALL_REJECT + } + + addPlugin(plugin: FlutterPlugin): void { + this.flutterEngine?.getPlugins()?.add(plugin) + } + + removePlugin(plugin: FlutterPlugin): void { + this.flutterEngine?.getPlugins()?.remove(plugin.getUniqueClassName()) + } + + isFlutterEngineFromHost(): boolean { + return this.isFlutterEngineFromHostOrCache; + } + + initWindow() { + if (this.flutterEngine && this.isAttached) { + this.platformPlugin?.initWindow() + } + } +} + +/** + * FlutterAbility句柄 + */ +interface Host extends FlutterEngineProvider, FlutterEngineConfigurator, PlatformPluginDelegate { + + getAbility(): UIAbility; + + shouldDispatchAppLifecycleState(): boolean; + + detachFromFlutterEngine(): void; + + shouldAttachEngineToAbility(): boolean; + + getCachedEngineId(): string; + + getCachedEngineGroupId(): string | null; + + /** + * Returns true if the {@link io.flutter.embedding.engine.FlutterEngine} used in this delegate + * should be destroyed when the host/delegate are destroyed. + */ + shouldDestroyEngineWithHost(): boolean; + + /** Returns the {@link FlutterShellArgs} that should be used when initializing Flutter. */ + getFlutterShellArgs(): FlutterShellArgs; + + /** Returns arguments that passed as a list of string to Dart's entrypoint function. */ + getDartEntrypointArgs(): Array; + + /** + * Returns the URI of the Dart library which contains the entrypoint method (example + * "package:foo_package/main.dart"). If null, this will default to the same library as the + * `main()` function in the Dart program. + */ + getDartEntrypointLibraryUri(): string; + + /** Returns the path to the app bundle where the Dart code exists. */ + getAppBundlePath(): string; + + /** + * Returns the Dart entrypoint that should run when a new {@link + * io.flutter.embedding.engine.FlutterEngine} is created. + */ + getDartEntrypointFunctionName(): string; + + /** Returns the initial route that Flutter renders. */ + getInitialRoute(): string; + + getWant(): Want; + + shouldRestoreAndSaveState(): boolean; + + getExclusiveAppComponent(): ExclusiveAppComponent | null + + providePlatformPlugin(flutterEngine: FlutterEngine): PlatformPlugin | undefined + + /** + * Whether to automatically attach the {@link FlutterView} to the engine. + * + *

In the add-to-app scenario where multiple {@link FlutterView} share the same {@link + * FlutterEngine}, the host application desires to determine the timing of attaching the {@link + * FlutterView} to the engine, for example, during the {@code onResume} instead of the {@code + * onCreateView}. + * + *

Defaults to {@code true}. + */ + attachToEngineAutomatically(): boolean; +} + +export { Host, FlutterAbilityAndEntryDelegate } \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbilityLaunchConfigs.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbilityLaunchConfigs.ets new file mode 100644 index 0000000000000000000000000000000000000000..bd3605159ef6e3db37dfbf490792edf4b55f7cf7 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbilityLaunchConfigs.ets @@ -0,0 +1,47 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +/** The mode of the background of a Flutter {@code Activity}, either opaque or transparent. */ +enum BackgroundMode { + /** Indicates a FlutterActivity with an opaque background. This is the default. */ + opaque, + /** Indicates a FlutterActivity with a transparent background. */ + transparent +} + +export default class FlutterAbilityLaunchConfigs { + static DART_ENTRYPOINT_META_DATA_KEY = "io.flutter.Entrypoint"; + static DART_ENTRYPOINT_URI_META_DATA_KEY = "io.flutter.EntrypointUri"; + static INITIAL_ROUTE_META_DATA_KEY = "io.flutter.InitialRoute"; + static SPLASH_SCREEN_META_DATA_KEY = "io.flutter.embedding.android.SplashScreenDrawable"; + static NORMAL_THEME_META_DATA_KEY = "io.flutter.embedding.android.NormalTheme"; + static HANDLE_DEEPLINKING_META_DATA_KEY = "flutter_deeplinking_enabled"; + // Intent extra arguments. + static EXTRA_DART_ENTRYPOINT = "dart_entrypoint"; + static EXTRA_DART_ENTRYPOINT_LIBRARY_URI = "dart_entrypoint_library_uri"; + static EXTRA_INITIAL_ROUTE = "route"; + static EXTRA_BACKGROUND_MODE = "background_mode"; + static EXTRA_CACHED_ENGINE_ID = "cached_engine_id"; + static EXTRA_DART_ENTRYPOINT_ARGS = "dart_entrypoint_args"; + static EXTRA_CACHED_ENGINE_GROUP_ID = "cached_engine_group_id"; + static EXTRA_DESTROY_ENGINE_WITH_ACTIVITY = "destroy_engine_with_activity"; + static EXTRA_ENABLE_STATE_RESTORATION = "enable_state_restoration"; + // Default configuration. + static DEFAULT_DART_ENTRYPOINT = "main"; + static DEFAULT_INITIAL_ROUTE = "/"; + static DEFAULT_BACKGROUND_MODE = BackgroundMode.opaque; + // Preload configuration. + static PRELOAD_VIEWPORT_METRICS_KEY = "viewport_metrics"; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterEngineConfigurator.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterEngineConfigurator.ets new file mode 100644 index 0000000000000000000000000000000000000000..6e0cabafbcd83021e6a28c7cb2117d5fd508a6cd --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterEngineConfigurator.ets @@ -0,0 +1,22 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import FlutterEngine from '../engine/FlutterEngine'; + +export default interface FlutterEngineConfigurator { + + configureFlutterEngine: (flutterEngine: FlutterEngine) => void; + cleanUpFlutterEngine: (flutterEngine: FlutterEngine) => void; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterEngineProvider.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterEngineProvider.ets new file mode 100644 index 0000000000000000000000000000000000000000..a8eea36514d690e00aba5c987f61907cf4292515 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterEngineProvider.ets @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import FlutterEngine from '../engine/FlutterEngine'; +import common from '@ohos.app.ability.common'; + +export default interface FlutterEngineProvider { + provideFlutterEngine(context: common.Context): FlutterEngine | null; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterEntry.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterEntry.ets new file mode 100644 index 0000000000000000000000000000000000000000..454f9e2cdee6f411b7be2bbabf9f9cbeff67e7dc --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterEntry.ets @@ -0,0 +1,244 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import FlutterEngine from '../engine/FlutterEngine'; +import PlatformPlugin from '../../plugin/PlatformPlugin'; +import Want from '@ohos.app.ability.Want'; +import FlutterShellArgs from '../engine/FlutterShellArgs'; +import UIAbility from '@ohos.app.ability.UIAbility'; +import ExclusiveAppComponent from './ExclusiveAppComponent'; +import { FlutterAbilityAndEntryDelegate, Host } from './FlutterAbilityAndEntryDelegate'; +import FlutterAbilityLaunchConfigs from './FlutterAbilityLaunchConfigs'; +import Log from '../../util/Log'; +import { FlutterView } from '../../view/FlutterView'; +import FlutterManager from './FlutterManager'; +import window from '@ohos.window'; +import FlutterEngineConfigurator from './FlutterEngineConfigurator'; +import { FlutterPlugin } from '../engine/plugins/FlutterPlugin'; + +const TAG = "FlutterEntry"; + +export default class FlutterEntry implements Host { + private static ARG_SHOULD_ATTACH_ENGINE_TO_ABILITY: string = "should_attach_engine_to_ability"; + protected uiAbility: UIAbility | null = null + protected delegate: FlutterAbilityAndEntryDelegate | null = null + protected flutterView: FlutterView | null = null + protected context: Context; + protected windowStage: window.WindowStage | null = null + private parameters: Record = {}; + protected engineConfigurator: FlutterEngineConfigurator | null = null + protected hasInit: boolean = false; + + constructor(context: Context, params: Record = {}) { + this.context = context; + this.uiAbility = FlutterManager.getInstance().getUIAbility(context); + this.parameters = params; + this.windowStage = FlutterManager.getInstance().getWindowStage(this.uiAbility); + this.hasInit = false; + } + + protected windowStageEventCallback = (data: window.WindowStageEventType) => { + this.delegate?.onWindowStageChanged(data) + } + + aboutToAppear() { + Log.i(TAG, 'aboutToAppear'); + if (this.hasInit == false) { + this.delegate = new FlutterAbilityAndEntryDelegate(this); + this.flutterView = this.delegate?.createView(this.context); + this.flutterView?.onWindowCreated(); + this?.delegate?.onAttach(this.context); + //this.flutterView?.preDraw(); + //Log.d(TAG, "XComponent aboutToAppear predraw"); + Log.i(TAG, 'onAttach end'); + this?.delegate?.platformPlugin?.setUIAbilityContext(this.uiAbility!!.context); + this.delegate?.onWindowStageCreate() + this.windowStage?.on('windowStageEvent', this.windowStageEventCallback); + this.hasInit = true; + this.delegate?.initWindow(); + } + } + + setFlutterEngineConfigurator(configurator: FlutterEngineConfigurator) { + this.engineConfigurator = configurator; + } + + getFlutterView(): FlutterView { + return this.flutterView!! + } + + getFlutterEngine(): FlutterEngine | null { + return this.delegate?.flutterEngine! + } + + aboutToDisappear() { + Log.d(TAG, "FlutterEntry aboutToDisappear"); + try { + this.windowStage?.off('windowStageEvent', this.windowStageEventCallback); + } catch (err) { + Log.e(TAG, "windowStage off failed"); + } + if (this.flutterView != null) { + this.flutterView.onDestroy(); + this.flutterView = null; + } + if (this.delegate != null) { + this.delegate?.onDetach(); + this.delegate?.release() + } + } + + onPageShow() { //生命周期 + Log.d(TAG, "FlutterEntry onPageShow"); + this?.delegate?.onShow(); + } + + onPageHide() { //生命周期 + Log.d(TAG, "FlutterEntry onPageHide"); + this?.delegate?.onHide(); + } + + onBackPress() { + Log.d(TAG, "FlutterEntry onBackPress"); + this?.delegate?.flutterEngine?.getNavigationChannel()?.popRoute(); + } + + shouldDispatchAppLifecycleState(): boolean { + return true; + } + + detachFromFlutterEngine() { + if (this?.delegate != null) { + this?.delegate?.onDetach(); + } + } + + getAbility(): UIAbility { + return this.uiAbility!! + } + + loadContent() { + + } + + shouldAttachEngineToAbility(): boolean { + return this.parameters![FlutterEntry.ARG_SHOULD_ATTACH_ENGINE_TO_ABILITY] as boolean + } + + getCachedEngineId(): string { + return this.parameters![FlutterAbilityLaunchConfigs.EXTRA_CACHED_ENGINE_ID] as string + } + + getCachedEngineGroupId(): string | null { + return this.parameters![FlutterAbilityLaunchConfigs.EXTRA_CACHED_ENGINE_GROUP_ID] as string + } + + shouldDestroyEngineWithHost(): boolean { + if ((this.getCachedEngineId() != null && this.getCachedEngineId().length > 0) || + this.delegate!!.isFlutterEngineFromHost()) { + // Only destroy a cached engine if explicitly requested by app developer. + return false; + } + return true; + } + + attachToEngineAutomatically(): boolean { + return true; + } + + getFlutterShellArgs(): FlutterShellArgs { + return new FlutterShellArgs(); + } + + getDartEntrypointArgs(): string[] { + if (this.parameters![FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT_ARGS]) { + return this.parameters![FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT_ARGS] as Array; + } + return new Array() + } + + getDartEntrypointLibraryUri(): string { + return ""; + } + + getAppBundlePath(): string { + return ""; + } + + getDartEntrypointFunctionName(): string { + if (this.parameters![FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT]) { + return this.parameters![FlutterAbilityLaunchConfigs.EXTRA_DART_ENTRYPOINT] as string; + } + return FlutterAbilityLaunchConfigs.DEFAULT_DART_ENTRYPOINT + } + + getInitialRoute(): string { + if (this.parameters![FlutterAbilityLaunchConfigs.EXTRA_INITIAL_ROUTE]) { + return this.parameters![FlutterAbilityLaunchConfigs.EXTRA_INITIAL_ROUTE] as string + } + return ""; + } + + getWant(): Want { + return new Want(); + } + + shouldRestoreAndSaveState(): boolean { + if (this.parameters![FlutterAbilityLaunchConfigs.EXTRA_CACHED_ENGINE_ID] != undefined) { + return this.parameters![FlutterAbilityLaunchConfigs.EXTRA_CACHED_ENGINE_ID] as boolean; + } + if (this.getCachedEngineId() != null && this.getCachedEngineId().length > 0) { + // Prevent overwriting the existing state in a cached engine with restoration state. + return false; + } + return true; + } + + getExclusiveAppComponent(): ExclusiveAppComponent | null { + return this.delegate ? this.delegate : null + } + + provideFlutterEngine(context: Context): FlutterEngine | null { + return null; + } + + providePlatformPlugin(flutterEngine: FlutterEngine): PlatformPlugin | undefined { + return new PlatformPlugin(flutterEngine.getPlatformChannel()!, this.context, this); + } + + configureFlutterEngine(flutterEngine: FlutterEngine) { + if (this.engineConfigurator) { + this.engineConfigurator.configureFlutterEngine(flutterEngine) + } + } + + cleanUpFlutterEngine(flutterEngine: FlutterEngine) { + if (this.engineConfigurator) { + this.engineConfigurator.cleanUpFlutterEngine(flutterEngine) + } + } + + popSystemNavigator(): boolean { + return false; + } + + addPlugin(plugin: FlutterPlugin): void { + this.delegate?.addPlugin(plugin) + } + + removePlugin(plugin: FlutterPlugin): void { + this.delegate?.removePlugin(plugin) + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterManager.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterManager.ets new file mode 100644 index 0000000000000000000000000000000000000000..da03c6adffe709c46117486be32b7a37a1622c20 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterManager.ets @@ -0,0 +1,248 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + +import { FlutterView } from '../../view/FlutterView'; +import UIAbility from '@ohos.app.ability.UIAbility'; +import window from '@ohos.window'; +import Log from '../../util/Log'; +import HashMap from '@ohos.util.HashMap'; +import List from '@ohos.util.List'; + +const TAG = "FlutterManager" + +export default class FlutterManager { + private static instance: FlutterManager; + + static getInstance(): FlutterManager { + if (FlutterManager.instance == null) { + FlutterManager.instance = new FlutterManager(); + } + return FlutterManager.instance; + } + + private flutterViewList = new Map(); + private flutterViewIndex = 1; + private uiAbilityList = new Array(); + private windowStageList = new Map(); + private mFullScreenListener: FullScreenListener = new DefaultFullScreenListener(); + + private dragEnterCbId: number = 1; + private dragMoveCbId: number = 1; + private dragLeaveCbId: number = 1; + private dropCbId: number = 1; + + private dragEnterCbs: HashMap = new HashMap(); + private dragMoveCbs: HashMap = new HashMap(); + private dragLeaveCbs: HashMap = new HashMap(); + private dropCbs: HashMap = new HashMap(); + + private getValuesFromMap(map: HashMap): List { + let list: List = new List(); + map.forEach((value, key) => { + list.add(value); + }); + return list; + } + + getDragEnterCbs(): List { + return this.getValuesFromMap(this.dragEnterCbs); + } + + getDragMoveCbs(): List { + return this.getValuesFromMap(this.dragMoveCbs); + } + + getDragLeaveCbs(): List { + return this.getValuesFromMap(this.dragLeaveCbs); + } + + getDropCbs(): List { + return this.getValuesFromMap(this.dropCbs); + } + + addDragEnterCb(callback: DragDropCallback): number { + this.dragEnterCbs.set(this.dragEnterCbId, callback); + return this.dragEnterCbId++; + } + + addDragMoveCb(callback: DragDropCallback): number { + this.dragMoveCbs.set(this.dragMoveCbId, callback); + return this.dragMoveCbId++; + } + + addDragLeaveCb(callback: DragDropCallback): number { + this.dragLeaveCbs.set(this.dragLeaveCbId, callback); + return this.dragLeaveCbId++; + } + + addDropCb(callback: DragDropCallback): number { + this.dropCbs.set(this.dropCbId, callback); + return this.dropCbId++; + } + + removeDragEnterCb(id: number) { + this.dragEnterCbs.remove(id); + } + + removeDragMoveCb(id: number) { + this.dragMoveCbs.remove(id); + } + + removeDragLeaveCb(id: number) { + this.dragLeaveCbs.remove(id); + } + + removeDropCb(id: number) { + this.dropCbs.remove(id); + } + + pushUIAbility(uiAbility: UIAbility) { + this.uiAbilityList.push(uiAbility); + } + + popUIAbility(uiAbility: UIAbility) { + let index = this.uiAbilityList.findIndex((item: UIAbility) => item == uiAbility) + if (index >= 0) { + this.uiAbilityList.splice(index, 1) + } + } + + pushWindowStage(uiAbility: UIAbility, windowStage: window.WindowStage) { + this.windowStageList.set(uiAbility, windowStage) + } + + popWindowStage(uiAbility: UIAbility) { + this.windowStageList.delete(uiAbility) + } + + getWindowStage(uiAbility: UIAbility): window.WindowStage { + return this.windowStageList.get(uiAbility)!! + } + + getUIAbility(context?: Context): UIAbility { + if (!context && this.uiAbilityList.length > 0) { + return this.uiAbilityList[0]; + } + return this.uiAbilityList.find((item: UIAbility) => item.context == context)!! + } + + hasFlutterView(viewId: string): boolean { + return this.flutterViewList.has(viewId); + } + + getFlutterView(viewId: string): FlutterView | null { + return this.flutterViewList.get(viewId) ?? null; + } + + getFlutterViewList(): Map { + return this.flutterViewList; + } + + private putFlutterView(viewId: string, flutterView?: FlutterView): void { + if (flutterView != null) { + this.flutterViewList.set(viewId, flutterView); + } else { + this.flutterViewList.delete(viewId); + } + } + + /** + * It's suggested to keep 'oh_flutter_' as the prefix for xcomponent_id. + * Otherwise it might affect the performance. + */ + createFlutterView(context: Context): FlutterView { + let flutterView = new FlutterView(`oh_flutter_${this.flutterViewIndex++}`, context); + this.putFlutterView(flutterView.getId(), flutterView); + return flutterView; + } + + getNextFlutterViewId(idOffset: number = 0): string { + return `oh_flutter_${this.flutterViewIndex + idOffset}`; + } + + clear(): void { + this.flutterViewList.clear(); + } + + setFullScreenListener(listener: FullScreenListener) { + this.mFullScreenListener = listener + } + + getFullScreenListener(): FullScreenListener { + return this.mFullScreenListener; + } + + setUseFullScreen(use: boolean, context?: Context | null | undefined) { + this.mFullScreenListener.setUseFullScreen(use, context); + } + + useFullScreen(): boolean { + return this.mFullScreenListener.useFullScreen(); + } +} + +export interface DragDropCallback { + do(event: DragEvent, extraParams: string): void; +} + +export interface FullScreenListener { + useFullScreen(): boolean; + + setUseFullScreen(useFullScreen: boolean, context?: Context | null | undefined): void; + + onScreenStateChanged(data: window.WindowStatusType): void; +} + +export class DefaultFullScreenListener implements FullScreenListener { + private fullScreen: boolean = true; + private skipCheck: boolean = false; + + useFullScreen(): boolean { + return this.fullScreen; + } + + setUseFullScreen(useFullScreen: boolean, context?: Context | null | undefined): void { + this.fullScreen = useFullScreen; + this.skipCheck = true; + + context = context??getContext(this); + let window = FlutterManager.getInstance() + .getWindowStage(FlutterManager.getInstance().getUIAbility(context)); + window.getMainWindowSync().setWindowLayoutFullScreen(useFullScreen); + Log.i(TAG, "WindowLayoutFullScreen is on") + } + + onScreenStateChanged(data: window.WindowStatusType): void { + if (this.skipCheck) { + Log.i(TAG, "onScreenStateChanged: skipCheck is on, WindowStatusType = " + data) + return; + } + switch (data) { + case window.WindowStatusType.FULL_SCREEN: + + case window.WindowStatusType.SPLIT_SCREEN: + case window.WindowStatusType.FLOATING: + case window.WindowStatusType.MAXIMIZE: + this.fullScreen = true; + Log.i(TAG, "onScreenStateChanged: fullScreen = true") + break; + default: + this.fullScreen = false; + Log.i(TAG, "onScreenStateChanged: fullScreen = false") + break; + } + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterPage.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterPage.ets new file mode 100644 index 0000000000000000000000000000000000000000..c969e6ebc28e14b1e5a1d419b5bea021f653c13a --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterPage.ets @@ -0,0 +1,280 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import Log from '../../util/Log'; +import { FlutterView } from '../../view/FlutterView'; +import FlutterManager from './FlutterManager'; +import { DVModel, DVModelChildren, DynamicView } from '../../view/DynamicView/dynamicView'; +import Any from '../../plugin/common/Any'; +import flutter from 'libflutter.so'; + +const TAG = "FlutterPage"; + + +/** + * 基础page组件,承载XComponent组件 + */ +@Component +export struct FlutterPage { + @Prop viewId: string = "" + @Prop xComponentType: XComponentType = XComponentType.SURFACE + /** + * renderFit under XComponent has a default setting of RESIZE_FILL. + * If the size of XComponent may change, this property needs to be passed in and set to a size-preserving property, + * such as TOP_LEFT. + */ + @Prop xComponentRenderFit: RenderFit = RenderFit.RESIZE_FILL; + + /** + * A switch for enabling the frame cache. + * When it is true, one frame of response latency will be increased in exchange for higher smoothness, + * and occasional timeouts in rendering frame submissions will not result in frame dropping. + */ + @Prop enableFrameCacheForSmooth: boolean = true; + + @Builder + doNothingBuilder() { + } + + @BuilderParam splashScreenView: () => void = this.doNothingBuilder; + + @Builder + defaultPage() { + Stack() { + ForEach(this.rootDvModel!!, (child: ESObject) => { + DynamicView({ + model: child as DVModel, + params: child.params, + events: child.events, + children: child.children, + customBuilder: child.builder + }) + }, (child: ESObject) => `${child.id_}`) + + Text("").id("unfocus-xcomponent-node").focusable(true) + + XComponent({ id: this.viewId, type: this.xComponentType, libraryname: 'flutter' }) + .id(this.viewId) + .focusable(true) + .onLoad((context) => { + this.flutterView?.onSurfaceCreated(); + // Callback is triggered when the xcomponent window is partially visible or completely hidden. + this.getUIContext()?.getAttachedFrameNodeById(this.viewId)?.commonEvent.setOnVisibleAreaApproximateChange( + { ratios: [0.0, 1.0], expectedUpdateInterval: 0 }, + (isExpanding: boolean, currentRatio: number) => { + if (isExpanding) { + Log.i(TAG, "setOnVisibleAreaApproximateChange -> xcomponentId: " + this.viewId + + " isExpanding: " + isExpanding + " ratio: " + currentRatio); + flutter.nativeUpdateCurrentXComponentId(this.viewId); + } + } + ) + Log.d(TAG, "XComponent onLoad "); + }) + .onDestroy(() => { + Log.d(TAG, "XComponent onDestroy "); + this.flutterView?.onSurfaceDestroyed() + }) + .renderFit(this.xComponentRenderFit) + .backgroundColor(this.firstFrameDisplayed && this.xComponentRenderFit == RenderFit.RESIZE_FILL ? + this.xComponentColor : Color.Transparent) + + if (this.showSplashScreen) { + this.splashScreenView(); + } + } + .defaultFocus(true) + .onKeyPreIme((event: KeyEvent) => { + return this.flutterView?.onKeyPreIme(event) ?? false; + }) + .onKeyEvent((event: KeyEvent) => { + return this.flutterView?.onKeyEvent(event) ?? false; + }) + .onDragEnter((event: DragEvent, extraParams: string) => { + FlutterManager.getInstance().getDragEnterCbs().forEach(dragEnterCb => { + dragEnterCb.do(event, extraParams); + }); + Log.d(TAG, "onDragEnter"); + }) + .onDragMove((event: DragEvent, extraParams: string) => { + FlutterManager.getInstance().getDragMoveCbs().forEach(dragMoveCb => { + dragMoveCb.do(event, extraParams); + }); + Log.d(TAG, "onDragMove"); + }) + .onDragLeave((event: DragEvent, extraParams: string) => { + FlutterManager.getInstance().getDragLeaveCbs().forEach(dragLeaveCb => { + dragLeaveCb.do(event, extraParams); + }); + Log.d(TAG, "onDragLeave"); + }) + .onDrop((event: DragEvent, extraParams: string) => { + FlutterManager.getInstance().getDropCbs().forEach(dropCb => { + dropCb.do(event, extraParams); + }); + Log.d(TAG, "onDrop"); + }) + } + + @Builder + mouseWheelPage() { + Stack() { + ForEach(this.rootDvModel!!, (child: Any) => { + DynamicView({ + model: child as DVModel, + params: child.params, + events: child.events, + children: child.children, + customBuilder: child.builder + }) + }, (child: ESObject) => `${child.id_}`) + + Text("").id("unfocus-xcomponent-node").focusable(true) + + XComponent({ id: this.viewId, type: this.xComponentType, libraryname: 'flutter' }) + .id(this.viewId) + .focusable(true) + .onLoad((context) => { + this.flutterView?.onSurfaceCreated(); + // Callback is triggered when the xcomponent window is partially visible or completely hidden. + this.getUIContext()?.getAttachedFrameNodeById(this.viewId)?.commonEvent.setOnVisibleAreaApproximateChange( + { ratios: [0.0, 1.0], expectedUpdateInterval: 0 }, + (isExpanding: boolean, currentRatio: number) => { + if (isExpanding) { + Log.i(TAG, "setOnVisibleAreaApproximateChange -> xcomponentId: " + this.viewId + + " isExpanding: " + isExpanding + " ratio: " + currentRatio); + flutter.nativeUpdateCurrentXComponentId(this.viewId); + } + } + ) + Log.d(TAG, "XComponent onLoad "); + }) + .onDestroy(() => { + Log.d(TAG, "XComponent onDestroy "); + this.flutterView?.onSurfaceDestroyed() + }) + .renderFit(this.xComponentRenderFit) + .backgroundColor(this.firstFrameDisplayed && this.xComponentRenderFit == RenderFit.RESIZE_FILL ? + this.xComponentColor : Color.Transparent) + + if (this.showSplashScreen) { + this.splashScreenView(); + } + } + .defaultFocus(true) + .onKeyPreIme((event: KeyEvent) => { + return this.flutterView?.onKeyPreIme(event) ?? false; + }) + .onKeyEvent((event: KeyEvent) => { + this.flutterView?.onKeyEvent(event) + }) + .onDragEnter((event: DragEvent, extraParams: string) => { + FlutterManager.getInstance().getDragEnterCbs().forEach(dragEnterCb => { + dragEnterCb.do(event, extraParams); + }); + Log.d(TAG, "onDragEnter"); + }) + .onDragMove((event: DragEvent, extraParams: string) => { + FlutterManager.getInstance().getDragMoveCbs().forEach(dragMoveCb => { + dragMoveCb.do(event, extraParams); + }); + Log.d(TAG, "onDragMove"); + }) + .onDragLeave((event: DragEvent, extraParams: string) => { + FlutterManager.getInstance().getDragLeaveCbs().forEach(dragLeaveCb => { + dragLeaveCb.do(event, extraParams); + }); + Log.d(TAG, "onDragLeave"); + }) + .onDrop((event: DragEvent, extraParams: string) => { + FlutterManager.getInstance().getDropCbs().forEach(dropCb => { + dropCb.do(event, extraParams); + }); + Log.d(TAG, "onDrop"); + }) + .gesture( + PanGesture(this.panOption) + .onActionStart((event: GestureEvent) => { + this.flutterView?.onMouseWheel("actionStart", event); + }) + .onActionUpdate((event: GestureEvent) => { + this.flutterView?.onMouseWheel("actionUpdate", event); + }) + .onActionEnd((event: GestureEvent) => { + this.flutterView?.onMouseWheel("actionEnd", event); + }) + ) + } + + @State showSplashScreen: boolean = true; + /** + * To address the black(or other color set by usr) flashing frame when switching between ArkUI and Flutter pages, + * the background color should be kept transparent until the onFirstFrame is called. + * When the window size changes, modifying the renderFit property in the relevant callback does not take effect immediately. + * The first frame will use the old renderFit property and the old background color, resulting in visual artifacts (such as stretching or a black screen). + * Therefore, we cannot automatically change the relevant properties through state variables at this time. + */ + @State firstFrameDisplayed: boolean = false; + @State xComponentColor: Color = Color.Black + + @State checkFullScreen: boolean = true; + @State checkKeyboard: boolean = true; + @State checkGesture: boolean = true; + @State checkMouseWheel: boolean = true; + @State checkAiBar: boolean = true; + + @StorageLink('nodeWidth') storageLinkWidth: number = 0; + @StorageLink('nodeHeight') storageLinkHeight: number = 0; + + @State rootDvModel: DVModelChildren | undefined = undefined + + private flutterView?: FlutterView | null + private panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Up | PanDirection.Down }); + + aboutToAppear() { + this.flutterView = FlutterManager.getInstance().getFlutterView(this.viewId); + this.flutterView?.addFirstFrameListener(this) + this.flutterView?.addFirstPreloadFrameListener(this) + + this.flutterView?.setCheckFullScreen(this.checkFullScreen) + this.flutterView?.setCheckKeyboard(this.checkKeyboard) + this.flutterView?.setCheckGesture(this.checkGesture) + this.flutterView?.setCheckAiBar(this.checkAiBar) + this.flutterView?.enableFrameCache(this.enableFrameCacheForSmooth); + + this.rootDvModel = this.flutterView!!.getDVModel().children + } + + aboutToDisappear() { + this.flutterView?.removeFirstFrameListener(this); + this.flutterView?.removeFirstPreloadFrameListener(this) + } + + onFirstFrame() { + this.showSplashScreen = false; + this.firstFrameDisplayed = true; + } + + onFirstPreloadFrame() { + } + + build() { + if (this.checkMouseWheel) { + this.mouseWheelPage(); + } else { + this.defaultPage(); + } + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyData.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyData.ets new file mode 100644 index 0000000000000000000000000000000000000000..0e765035a53a3895f95ed88380b54b9e40fc05bf --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyData.ets @@ -0,0 +1,124 @@ +/* +* Copyright (C) Huawei Technologies Co., Ltd. 2024. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import util from '@ohos.util' + +export default class KeyData { + private static TAG = "KeyData"; + public static CHANNEL = "flutter/keydata"; + // If this value changes, update the code in the following files: + // + // * key_data.h (kKeyDataFieldCount) + // * platform_dispatcher.dart (_kKeyDataFieldCount) + private static FIELD_COUNT: number = 6; + private static BYTES_PER_FIELD: number = 8; + public timestamp: number = 0; + public type: Type = Type.KDOWN; + public physicalKey: number = 0; + public logicalKey: number = 0; + public isSynthesized: boolean = false; + public deviceType: DeviceType = DeviceType.KKEYBOARD; + public character: string | null = null; + + constructor(buffer?: ArrayBuffer) { + if (buffer !== undefined) { + const view = new DataView(buffer); + let offset = 0; + + const decoder = new util.TextDecoder("utf-8"); + const charSize = Number(view.getBigInt64(offset, true)); + offset += 8; + + this.timestamp = Number(view.getBigInt64(offset, true)); + offset += 8; + + this.type = Number(view.getBigInt64(offset, true)) as Type; + offset += 8; + + this.physicalKey = Number(view.getBigInt64(offset, true)); + offset += 8; + + this.logicalKey = Number(view.getBigInt64(offset, true)); + offset += 8; + + this.isSynthesized = view.getBigInt64(offset, true) === BigInt(1); + offset += 8; + + this.deviceType = Number(view.getBigInt64(offset, true)) as DeviceType; + offset += 8; + + if (offset + charSize !== buffer.byteLength) { + throw new Error("KeyData corruption: String length does not match remaining bytes in buffer"); + } + + if (charSize != 0) { + const strBytes = new Uint8Array(buffer, offset, charSize); + this.character = decoder.decode(strBytes); + } + } + } + + public toBytes(): ArrayBuffer { + const encoder = new util.TextEncoder("utf-8"); + const encodedCharBytes = this.character == null ? null : encoder.encode(this.character); + const charSize = this.character == null ? 0 : this.character.length; + + const totalBytes = (KeyData.FIELD_COUNT + 1) * KeyData.BYTES_PER_FIELD + charSize; + const buffer = new ArrayBuffer(totalBytes); + const view = new DataView(buffer); + let offset = 0; + + view.setBigInt64(offset, BigInt(charSize), true); + offset += 8; + + view.setBigInt64(offset, BigInt(this.timestamp), true); + offset += 8; + + view.setBigInt64(offset, BigInt(this.type), true); + offset += 8; + + view.setBigInt64(offset, BigInt(this.physicalKey), true); + offset += 8; + + view.setBigInt64(offset, BigInt(this.logicalKey), true); + offset += 8; + + view.setBigInt64(offset, this.isSynthesized ? BigInt(1) : BigInt(0), true); + offset += 8; + + view.setBigInt64(offset, BigInt(this.deviceType), true); + offset += 8; + + if (encodedCharBytes != null) { + new Uint8Array(buffer, offset, charSize).set(encodedCharBytes); + } + + return buffer; + } +} + +export enum Type { + KDOWN = 0, + KUP, + KREPEAT +} + +export enum DeviceType { + KKEYBOARD = 0, + KDIRECTIONALPAD, + KGAMEPAD, + KJOYSTICK, + KHDMI +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyEmbedderResponder.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyEmbedderResponder.ets new file mode 100644 index 0000000000000000000000000000000000000000..2f70b1bac02671fe8f6a6d5a6cd1ea04a088cccc --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyEmbedderResponder.ets @@ -0,0 +1,274 @@ +/* +* Copyright (C) Huawei Technologies Co., Ltd. 2024. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { BinaryMessenger, BinaryReply } from '../../plugin/common/BinaryMessenger'; +import KeyData, { Type, DeviceType } from './KeyData'; +import { Responder } from './KeyboardManager'; +import KeyboardMap, { KeyPair, ModifierGoal } from './KeyboardMap'; +import Log from '../../util/Log'; + +class EventTaskRunner { + private tasks: Array<() => void> = []; + + constructor() { + } + + public addTask(task: () => void): void { + this.tasks.push(task); + } + + public runTasks(): void { + this.tasks.forEach(task => task()); + } +} + +export default class KeyEmbedderResponder implements Responder { + private static TAG = "KeyEmbedderResponder"; + private messenger: BinaryMessenger; + private pressingRecords: Map = new Map(); + + constructor(binaryMessenger: BinaryMessenger) { + this.messenger = binaryMessenger; + } + + private keyOfPlane(key: number, plane: number): number { + return plane | (key & KeyboardMap.kValueMask); + } + + private getEventType(event: KeyEvent): Type { + let physicalKey: number = this.getPhysicalKey(event); + let isPressed: boolean = this.pressingRecords.has(physicalKey); + switch (event.type) { + case KeyType.Down: + return isPressed ? Type.KREPEAT : Type.KDOWN; + break; + case KeyType.Up: + return Type.KUP; + break; + default: + throw new Error("getEventType: Unexpected event type"); + } + } + + private getLogicalKey(event: KeyEvent): number { + let keyCode: number = event.keyCode; + let logicalKey: number | undefined = KeyboardMap.toLogicalKey.get(keyCode); + if (logicalKey !== undefined) { + return logicalKey; + } + return this.keyOfPlane(keyCode, KeyboardMap.kOhosPlane); + } + + /** Should use ScanCode here, but cannot access it from KeyEvent yet. + * Use KeyCode instead and need correct it in the future. + */ + private getPhysicalKey(event: KeyEvent): number { + let keyCode: number = event.keyCode; + let physicalKey: number | undefined = KeyboardMap.toPhysicalKey.get(keyCode); + if (physicalKey !== undefined) { + return physicalKey; + } + return this.keyOfPlane(keyCode, KeyboardMap.kOhosPlane); + } + + updatePressingKeys(physicalKey: number, logicalKey: number | null): void { + if (logicalKey != null) { // press + if (this.pressingRecords.has(physicalKey)) { + Log.e(KeyEmbedderResponder.TAG, "updatePressingKeys adding nonempty key"); + } + this.pressingRecords.set(physicalKey, logicalKey); + } else { // release + if (!this.pressingRecords.has(physicalKey)) { + Log.e(KeyEmbedderResponder.TAG, "updatePressingKeys deleting empty key"); + } + this.pressingRecords.delete(physicalKey); + } + } + + synchronizeModifierKey(goal: ModifierGoal, + truePressed: boolean, + logicalKey: number, + physicalKey: number, + event: KeyEvent, + postSyncEvents: EventTaskRunner) { + let nowStates: boolean[] = new Array(goal.keys.length); + let expectedPreStates: boolean[] = new Array(goal.keys.length); + let postAnyPressed: boolean = false; + + for (let keyIdx = 0; keyIdx < goal.keys.length; keyIdx += 1) { + let key: KeyPair = goal.keys[keyIdx]; + nowStates[keyIdx] = this.pressingRecords.has(key.physicalKey); + if (key.logicalKey == logicalKey) { + switch (this.getEventType(event)) { + case Type.KDOWN: + expectedPreStates[keyIdx] = false; + postAnyPressed = true; + if (!truePressed) { + postSyncEvents.addTask(() => { + this.synthesizeEvent(false, event.timestamp, logicalKey, physicalKey); + }); + } + break; + case Type.KUP: + expectedPreStates[keyIdx] = nowStates[keyIdx]; + break; + case Type.KREPEAT: + expectedPreStates[keyIdx] = nowStates[keyIdx]; + postAnyPressed = true; + if (!truePressed) { + postSyncEvents.addTask(() => { + this.synthesizeEvent(false, event.timestamp, logicalKey, physicalKey); + }); + } + break; + } + } else { + postAnyPressed = postAnyPressed || nowStates[keyIdx]; + } + } + + if (truePressed) { + for (let keyIdx = 0; keyIdx < goal.keys.length; keyIdx += 1) { + if (expectedPreStates[keyIdx] !== undefined) { + continue; + } + if (postAnyPressed) { + expectedPreStates[keyIdx] = nowStates[keyIdx]; + } else { + expectedPreStates[keyIdx] = true; + postAnyPressed = true; + } + } + if (!postAnyPressed) { + expectedPreStates[0] = true; + } + } else { + for (let keyIdx = 0; keyIdx < goal.keys.length; keyIdx += 1) { + if (expectedPreStates[keyIdx] !== undefined) { + continue; + } + expectedPreStates[keyIdx] = false; + } + } + + for (let keyIdx = 0; keyIdx < goal.keys.length; keyIdx += 1) { + if (expectedPreStates[keyIdx] != nowStates[keyIdx]) { + let key: KeyPair = goal.keys[keyIdx]; + this.synthesizeEvent(expectedPreStates[keyIdx], event.timestamp, + key.logicalKey, key.physicalKey); + } + } + } + + synthesizeEvent(isDown: boolean, timestamp: number, + logicalKey: number, physicalKey: number) { + const data: KeyData = new KeyData(); + data.timestamp = timestamp; + data.type = isDown ? Type.KDOWN : Type.KUP; + data.logicalKey = logicalKey; + data.physicalKey = physicalKey; + data.character = null; + data.isSynthesized = true; + data.deviceType = DeviceType.KKEYBOARD; + if (physicalKey != 0 && logicalKey != 0) { + this.updatePressingKeys(physicalKey, isDown ? logicalKey : null); + } + + this.sendKeyEvent(data); + } + + sendKeyEvent(data: KeyData) { + this.messenger.send(KeyData.CHANNEL, data.toBytes()); + } + + handleKeyEvent(event: KeyEvent): boolean { + if (event.keyCode == 0) { + return false; + } + + let physicalKey: number = this.getPhysicalKey(event); + let logicalKey: number = this.getLogicalKey(event); + + let postSyncEvents: EventTaskRunner = new EventTaskRunner(); + + for (let goalIdx = 0; goalIdx < KeyboardMap.modifierGoals.length; goalIdx += 1) { + let goal: ModifierGoal = KeyboardMap.modifierGoals[goalIdx]; + if (event.getModifierKeyState != undefined) { + this.synchronizeModifierKey( + goal, + event.getModifierKeyState([goal.name]), + logicalKey, + physicalKey, + event, + postSyncEvents + ); + } + } + + let isDownEvent: boolean; + switch (event.type) { + case KeyType.Down: + isDownEvent = true; + break; + case KeyType.Up: + isDownEvent = false; + break; + default: + isDownEvent = false; + } + + let type: Type; + let lastLogicalKey: number | undefined = this.pressingRecords.get(physicalKey); + if (isDownEvent) { + if (lastLogicalKey === undefined) { + type = Type.KDOWN; + } else { + /* Nothing about repeat found in KeyEvent, so if isDownEvent and the key is + * currently pressed, take this event as a KREPEAT one. + */ + type = Type.KREPEAT; + } + } else { + if (lastLogicalKey === undefined) { + /* Ignore abrupt up events */ + return false; + } else { + type = Type.KUP; + } + } + + if (type != Type.KREPEAT) { + this.updatePressingKeys(physicalKey, isDownEvent ? logicalKey : null); + } + + const data: KeyData = new KeyData(); + data.timestamp = event.timestamp; + data.type = type; + data.physicalKey = physicalKey; + data.logicalKey = logicalKey; + data.character = null; + data.isSynthesized = false; + // no deviceType found in KeyEvent + data.deviceType = DeviceType.KKEYBOARD; + this.sendKeyEvent(data); + + postSyncEvents.runTasks(); + return true; + } + + public getPressedKeys(): Map { + return new Map(this.pressingRecords); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyEventHandler.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyEventHandler.ets new file mode 100644 index 0000000000000000000000000000000000000000..5e3afb980fd96d00e66db650a77a86ff29fd82d3 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyEventHandler.ets @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { HashMap } from '@kit.ArkTS'; +import deviceInfo from '@ohos.deviceInfo'; +import TextInputPlugin from '../../plugin/editing/TextInputPlugin'; +import Log from '../../util/Log'; +import { KeyCode } from '@kit.InputKit'; + +const TAG = "KeyEventHandler"; + +export class KeyEventHandler { + private textInputPlugin?: TextInputPlugin; + private charMap : HashMap = new HashMap(); + private shiftMap : HashMap = new HashMap(); + private isShiftMode: boolean = false; + + constructor(textInputPlugin?: TextInputPlugin) { + this.textInputPlugin = textInputPlugin; + this.initCharMap(); + this.initShiftMap(); + } + + private initCharMap() { + this.charMap.set(KeyCode.KEYCODE_0, '0') + this.charMap.set(KeyCode.KEYCODE_1, '1') + this.charMap.set(KeyCode.KEYCODE_2, '2') + this.charMap.set(KeyCode.KEYCODE_3, '3') + this.charMap.set(KeyCode.KEYCODE_4, '4') + this.charMap.set(KeyCode.KEYCODE_5, '5') + this.charMap.set(KeyCode.KEYCODE_6, '6') + this.charMap.set(KeyCode.KEYCODE_7, '7') + this.charMap.set(KeyCode.KEYCODE_8, '8') + this.charMap.set(KeyCode.KEYCODE_9, '9') + this.charMap.set(KeyCode.KEYCODE_A, 'a') + this.charMap.set(KeyCode.KEYCODE_B, 'b') + this.charMap.set(KeyCode.KEYCODE_C, 'c') + this.charMap.set(KeyCode.KEYCODE_D, 'd') + this.charMap.set(KeyCode.KEYCODE_E, 'e') + this.charMap.set(KeyCode.KEYCODE_F, 'f') + this.charMap.set(KeyCode.KEYCODE_G, 'g') + this.charMap.set(KeyCode.KEYCODE_H, 'h') + this.charMap.set(KeyCode.KEYCODE_I, 'i') + this.charMap.set(KeyCode.KEYCODE_J, 'j') + this.charMap.set(KeyCode.KEYCODE_K, 'k') + this.charMap.set(KeyCode.KEYCODE_L, 'l') + this.charMap.set(KeyCode.KEYCODE_M, 'm') + this.charMap.set(KeyCode.KEYCODE_N, 'n') + this.charMap.set(KeyCode.KEYCODE_O, 'o') + this.charMap.set(KeyCode.KEYCODE_P, 'p') + this.charMap.set(KeyCode.KEYCODE_Q, 'q') + this.charMap.set(KeyCode.KEYCODE_R, 'r') + this.charMap.set(KeyCode.KEYCODE_S, 's') + this.charMap.set(KeyCode.KEYCODE_T, 't') + this.charMap.set(KeyCode.KEYCODE_U, 'u') + this.charMap.set(KeyCode.KEYCODE_V, 'v') + this.charMap.set(KeyCode.KEYCODE_W, 'w') + this.charMap.set(KeyCode.KEYCODE_X, 'x') + this.charMap.set(KeyCode.KEYCODE_Y, 'y') + this.charMap.set(KeyCode.KEYCODE_Z, 'z') + this.charMap.set(KeyCode.KEYCODE_GRAVE, '`') + this.charMap.set(KeyCode.KEYCODE_MINUS, '-') + this.charMap.set(KeyCode.KEYCODE_EQUALS, '=') + this.charMap.set(KeyCode.KEYCODE_LEFT_BRACKET, '[') + this.charMap.set(KeyCode.KEYCODE_RIGHT_BRACKET, ']') + this.charMap.set(KeyCode.KEYCODE_BACKSLASH, '\\') + this.charMap.set(KeyCode.KEYCODE_SEMICOLON, ';') + this.charMap.set(KeyCode.KEYCODE_APOSTROPHE, '\'') + this.charMap.set(KeyCode.KEYCODE_COMMA, ',') + this.charMap.set(KeyCode.KEYCODE_PERIOD, '.') + this.charMap.set(KeyCode.KEYCODE_SLASH, '/') + this.charMap.set(KeyCode.KEYCODE_SPACE, ' ') + } + + private initShiftMap() { + this.shiftMap.set(KeyCode.KEYCODE_0, ')') + this.shiftMap.set(KeyCode.KEYCODE_1, '!') + this.shiftMap.set(KeyCode.KEYCODE_2, '@') + this.shiftMap.set(KeyCode.KEYCODE_3, '#') + this.shiftMap.set(KeyCode.KEYCODE_4, '$') + this.shiftMap.set(KeyCode.KEYCODE_5, '%') + this.shiftMap.set(KeyCode.KEYCODE_6, '^') + this.shiftMap.set(KeyCode.KEYCODE_7, '&') + this.shiftMap.set(KeyCode.KEYCODE_8, '*') + this.shiftMap.set(KeyCode.KEYCODE_9, '(') + this.shiftMap.set(KeyCode.KEYCODE_A, 'A') + this.shiftMap.set(KeyCode.KEYCODE_B, 'B') + this.shiftMap.set(KeyCode.KEYCODE_C, 'C') + this.shiftMap.set(KeyCode.KEYCODE_D, 'D') + this.shiftMap.set(KeyCode.KEYCODE_E, 'E') + this.shiftMap.set(KeyCode.KEYCODE_F, 'F') + this.shiftMap.set(KeyCode.KEYCODE_G, 'G') + this.shiftMap.set(KeyCode.KEYCODE_H, 'H') + this.shiftMap.set(KeyCode.KEYCODE_I, 'I') + this.shiftMap.set(KeyCode.KEYCODE_J, 'J') + this.shiftMap.set(KeyCode.KEYCODE_K, 'K') + this.shiftMap.set(KeyCode.KEYCODE_L, 'L') + this.shiftMap.set(KeyCode.KEYCODE_M, 'M') + this.shiftMap.set(KeyCode.KEYCODE_N, 'N') + this.shiftMap.set(KeyCode.KEYCODE_O, 'O') + this.shiftMap.set(KeyCode.KEYCODE_P, 'P') + this.shiftMap.set(KeyCode.KEYCODE_Q, 'Q') + this.shiftMap.set(KeyCode.KEYCODE_R, 'R') + this.shiftMap.set(KeyCode.KEYCODE_S, 'S') + this.shiftMap.set(KeyCode.KEYCODE_T, 'T') + this.shiftMap.set(KeyCode.KEYCODE_U, 'U') + this.shiftMap.set(KeyCode.KEYCODE_V, 'V') + this.shiftMap.set(KeyCode.KEYCODE_W, 'W') + this.shiftMap.set(KeyCode.KEYCODE_X, 'X') + this.shiftMap.set(KeyCode.KEYCODE_Y, 'Y') + this.shiftMap.set(KeyCode.KEYCODE_Z, 'Z') + this.shiftMap.set(KeyCode.KEYCODE_GRAVE, '~') + this.shiftMap.set(KeyCode.KEYCODE_MINUS, '_') + this.shiftMap.set(KeyCode.KEYCODE_EQUALS, '+') + this.shiftMap.set(KeyCode.KEYCODE_LEFT_BRACKET, '{') + this.shiftMap.set(KeyCode.KEYCODE_RIGHT_BRACKET, '}') + this.shiftMap.set(KeyCode.KEYCODE_BACKSLASH, '|') + this.shiftMap.set(KeyCode.KEYCODE_SEMICOLON, ':') + this.shiftMap.set(KeyCode.KEYCODE_APOSTROPHE, '"') + this.shiftMap.set(KeyCode.KEYCODE_COMMA, '<') + this.shiftMap.set(KeyCode.KEYCODE_PERIOD, '>') + this.shiftMap.set(KeyCode.KEYCODE_SLASH, '?') + this.shiftMap.set(KeyCode.KEYCODE_SPACE, ' ') + } + + getCharByEvent(event: KeyEvent) : string { + let key = event.keyCode; + if (this.isShiftMode) { + return this.shiftMap.hasKey(key) ? this.shiftMap.get(key) : '' + } else { + return this.charMap.hasKey(key) ? this.charMap.get(key) : '' + } + } + + handleKeyEvent(event: KeyEvent) { + Log.i(TAG, JSON.stringify({ + "name": "handleKeyEvent", + "event": event + })); + if (event.type == KeyType.Up) { + // 处理字符按键相关逻辑 + if (this.charMap.hasKey(event.keyCode)) { + this.textInputPlugin?.getEditingState().handleInsertTextEvent(this.getCharByEvent(event)) + } + // 处理非字符按键 + if (event.keyCode == KeyCode.KEYCODE_DEL) { + this.textInputPlugin?.getEditingState().handleDeleteEvent(false, 1) + } else if (event.keyCode == KeyCode.KEYCODE_FORWARD_DEL) { + this.textInputPlugin?.getEditingState().handleDeleteEvent(true, 1) + } + } + this.isShiftMode = (event.keyCode == KeyCode.KEYCODE_SHIFT_LEFT || event.keyCode == KeyCode.KEYCODE_SHIFT_RIGHT) + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyboardManager.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyboardManager.ets new file mode 100644 index 0000000000000000000000000000000000000000..bfae3539f859b6ce90a4eaf4272bb4cf118782f9 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyboardManager.ets @@ -0,0 +1,61 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import TextInputPlugin from '../../plugin/editing/TextInputPlugin'; +import FlutterEngine from '../engine/FlutterEngine'; +import KeyEventChannel, { FlutterKeyEvent } from '../engine/systemchannels/KeyEventChannel'; +import KeyboardChannel from '../engine/systemchannels/KeyboardChannel'; +import KeyEmbedderResponder from './KeyEmbedderResponder'; +import { BinaryMessenger } from '../../plugin/common/BinaryMessenger'; +import { KeyEventHandler } from './KeyEventHandler'; +import HashSet from '@ohos.util.HashSet'; + +export default class KeyboardManager { + private keyEventChannel: KeyEventChannel | null = null; + private keyboardChannel: KeyboardChannel | null = null; + protected keyEmbedderResponder: KeyEmbedderResponder; + private keyEventHandler: KeyEventHandler; + + constructor(engine: FlutterEngine, textInputPlugin: TextInputPlugin) { + this.keyEventChannel = new KeyEventChannel(engine.dartExecutor); + this.keyboardChannel = new KeyboardChannel(engine.dartExecutor); + this.keyboardChannel.setKeyboardMethodHandler(this); + this.keyEmbedderResponder = new KeyEmbedderResponder(engine.dartExecutor); + this.keyEventHandler = new KeyEventHandler(textInputPlugin); + } + + onKeyPreIme(event: KeyEvent) : boolean { + this.keyEmbedderResponder.handleKeyEvent(event); + + this.keyEventChannel?.sendFlutterKeyEvent(new FlutterKeyEvent(event), event.type == KeyType.Up, { + onFrameworkResponse: (isEventHandled: boolean): void => { + + } + }) + return false; + } + + onKeyEvent(event: KeyEvent) : boolean { + this.keyEventHandler.handleKeyEvent(event); + return false; + } + + public getKeyboardState(): Map { + return this.keyEmbedderResponder.getPressedKeys(); + } +} + +export interface Responder { + handleKeyEvent(keyEvent: KeyEvent): void; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyboardMap.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyboardMap.ets new file mode 100644 index 0000000000000000000000000000000000000000..b0141ce3936952277dc460832ca0d2e78c1a23ba --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/KeyboardMap.ets @@ -0,0 +1,613 @@ +/* +* Copyright (C) Huawei Technologies Co., Ltd. 2024. +* 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. +*/ + +export class KeyPair { + public physicalKey: number; + public logicalKey: number; + + constructor(physicalKey: number, logicalKey: number) { + this.physicalKey = physicalKey; + this.logicalKey = logicalKey; + } +} + +export class ModifierGoal { + public name: string; + public keys: KeyPair[]; + + constructor(name: string, keys: KeyPair[]) { + this.name = name; + this.keys = keys; + } +} + +export default class KeyboardMap { + /** Map OH KeyCode to Flutter LogicalKey. */ + public static toLogicalKey: Map = + new Map([ + [0x000000007D0, 0x00000000030], // digit0 + [0x000000007D1, 0x00000000031], // digit1 + [0x000000007D2, 0x00000000032], // digit2 + [0x000000007D3, 0x00000000033], // digit3 + [0x000000007D4, 0x00000000034], // digit4 + [0x000000007D5, 0x00000000035], // digit5 + [0x000000007D6, 0x00000000036], // digit6 + [0x000000007D7, 0x00000000037], // digit7 + [0x000000007D8, 0x00000000038], // digit8 + [0x000000007D9, 0x00000000039], // digit9 + [0x000000007DA, 0x0000000002A], // asterisk + [0x000000007DB, 0x00000000023], // numberSign + [0x000000007DC, 0x00100000304], // arrowUp + [0x000000007DD, 0x00100000301], // arrowDown + [0x000000007DE, 0x00100000302], // arrowLeft + [0x000000007DF, 0x00100000303], // arrowRight + [0x000000007E1, 0x00000000061], // keyA + [0x000000007E2, 0x00000000062], // keyB + [0x000000007E3, 0x00000000063], // keyC + [0x000000007E4, 0x00000000064], // keyD + [0x000000007E5, 0x00000000065], // keyE + [0x000000007E6, 0x00000000066], // keyF + [0x000000007E7, 0x00000000067], // keyG + [0x000000007E8, 0x00000000068], // keyH + [0x000000007E9, 0x00000000069], // keyI + [0x000000007EA, 0x0000000006A], // keyJ + [0x000000007EB, 0x0000000006B], // keyK + [0x000000007EC, 0x0000000006C], // keyL + [0x000000007ED, 0x0000000006D], // keyM + [0x000000007EE, 0x0000000006E], // keyN + [0x000000007EF, 0x0000000006F], // keyO + [0x000000007F0, 0x00000000070], // keyP + [0x000000007F1, 0x00000000071], // keyQ + [0x000000007F2, 0x00000000072], // keyR + [0x000000007F3, 0x00000000073], // keyS + [0x000000007F4, 0x00000000074], // keyT + [0x000000007F5, 0x00000000075], // keyU + [0x000000007F6, 0x00000000076], // keyV + [0x000000007F7, 0x00000000077], // keyW + [0x000000007F8, 0x00000000078], // keyX + [0x000000007F9, 0x00000000079], // keyY + [0x000000007FA, 0x0000000007A], // keyZ + [0x000000007FB, 0x0000000002C], // comma + [0x000000007FC, 0x0000000002E], // period + [0x000000007FD, 0x00200000104], // altLeft + [0x000000007FE, 0x00200000105], // altRight + [0x000000007FF, 0x00200000102], // shiftLeft + [0x00000000800, 0x00200000103], // shiftRight + [0x00000000801, 0x00100000009], // tab + [0x00000000802, 0x00000000020], // space + [0x00000000804, 0x00100000B09], // launchWebBrowser + [0x00000000805, 0x00100000B03], // launchMail + [0x00000000806, 0x0010000000D], // enter + [0x00000000807, 0x00100000008], // backspace + [0x00000000808, 0x00000000060], // backquote + [0x00000000809, 0x0000000002D], // minus + [0x0000000080A, 0x0000000003D], // equal + [0x0000000080B, 0x0000000005B], // bracketLeft + [0x0000000080C, 0x0000000005D], // bracketRight + [0x0000000080D, 0x0000000005C], // backslash + [0x0000000080E, 0x0000000003B], // semicolon + [0x0000000080F, 0x00000000022], // apostrophe/quote + [0x00000000810, 0x0000000002F], // slash + [0x00000000813, 0x00100000505], // contextMenu + [0x000000009A2, 0x00100000704], // compose + [0x00000000814, 0x00100000308], // pageUp + [0x00000000815, 0x00100000307], // pageDown + [0x00000000816, 0x0010000001B], // escape + [0x00000000817, 0x0010000007F], // delete + [0x00000000818, 0x00200000100], // controlLeft + [0x00000000819, 0x00200000101], // controlRight + [0x0000000081A, 0x00100000104], // capsLock + [0x0000000081B, 0x0010000010C], // scrollLock + [0x0000000081C, 0x00200000106], // metaLeft + [0x0000000081D, 0x00200000107], // metaRight + [0x0000000081E, 0x00100000106], // fn + [0x0000000081F, 0x00100000608], // printScreen + [0x00000000820, 0x00100000509], // pause + [0x00000000821, 0x00100000306], // home + [0x00000000822, 0x00100000305], // end + [0x00000000823, 0x00100000407], // insert + [0x00000000824, 0x00100000C03], // browserForward + [0x00000000825, 0x00100000D2F], // mediaPlay + [0x00000000A53, 0x0010000050A], // play + [0x00000000826, 0x00100000D2E], // mediaPause + [0x00000000827, 0x00100000D5B], // mediaClose + [0x00000000828, 0x00100000604], // eject + [0x00000000829, 0x00100000D30], // mediaRecord + [0x0000000082A, 0x00100000801], // f1 + [0x0000000082B, 0x00100000802], // f2 + [0x0000000082C, 0x00100000803], // f3 + [0x0000000082D, 0x00100000804], // f4 + [0x0000000082E, 0x00100000805], // f5 + [0x0000000082F, 0x00100000806], // f6 + [0x00000000830, 0x00100000807], // f7 + [0x00000000831, 0x00100000808], // f8 + [0x00000000832, 0x00100000809], // f9 + [0x00000000833, 0x0010000080A], // f10 + [0x00000000834, 0x0010000080B], // f11 + [0x00000000835, 0x0010000080C], // f12 + [0x00000000836, 0x0010000010A], // numLock + [0x00000000837, 0x00200000230], // numpad0 + [0x00000000838, 0x00200000231], // numpad1 + [0x00000000839, 0x00200000232], // numpad2 + [0x0000000083A, 0x00200000233], // numpad3 + [0x0000000083B, 0x00200000234], // numpad4 + [0x0000000083C, 0x00200000235], // numpad5 + [0x0000000083D, 0x00200000236], // numpad6 + [0x0000000083E, 0x00200000237], // numpad7 + [0x0000000083F, 0x00200000238], // numpad8 + [0x00000000840, 0x00200000239], // numpad9 + [0x00000000841, 0x0020000022F], // numpadDivide + [0x00000000842, 0x0020000022A], // numpadMultiply + [0x00000000843, 0x0020000022D], // numpadSubtract + [0x00000000844, 0x0020000022B], // numpadAdd + [0x00000000845, 0x0020000022E], // numpadDecimal + [0x00000000846, 0x0020000022C], // numpadComma + [0x00000000847, 0x0020000020D], // numpadEnter + [0x00000000848, 0x0020000023D], // numpadEqual + [0x00000000849, 0x00200000228], // numpadParenLeft + [0x0000000084A, 0x00200000229], // numpadParenRight + [0x00000000010, 0x00100000A10], // audioVolumeUp + [0x00000000011, 0x00100000A0F], // audioVolumeDown + [0x00000000012, 0x00100000606], // power + [0x00000000016, 0x00100000E09], // microphoneVolumeMute + [0x00000000001, 0x00100000306], // home + [0x00000000002, 0x00100001005], // goBack + [0x00000000013, 0x00100000603], // camera + [0x00000000028, 0x00100000602], // brightnessUp + [0x00000000029, 0x00100000601], // brightnessDown + [0x00000000005, 0x00100000401], // clear + [0x0000000000A, 0x00100000A05], // mediaPlayPause + [0x0000000000B, 0x00100000A07], // mediaStop + [0x0000000000C, 0x00100000A08], // mediaTrackNext + [0x0000000000D, 0x00100000A09], // mediaTrackPrevious + [0x0000000000E, 0x00100000D31], // mediaRewind + [0x0000000000F, 0x00100000D2C], // mediaFastForward + [0x00000000A28, 0x00200000002], // sleep + [0x00000000A29, 0x0010000071D], // zenkakuHankaku + [0x00000000A2C, 0x0010000071A], // katakana + [0x00000000A2D, 0x00100000716], // hiragana + [0x00000000A2E, 0x00100000705], // convert + [0x00000000A2F, 0x00100000717], // hiraganaKatakana + [0x00000000A30, 0x0010000070D], // nonConvert + [0x00000000A37, 0x00200000022], // intlYen + [0x00000000A39, 0x00100000502], // again + [0x00000000A3A, 0x0010000050B], // props + [0x00000000A3B, 0x0010000040A], // undo + [0x00000000A3C, 0x00100000402], // copy + [0x00000000A3D, 0x00100000A0B], // open + [0x00000000A3E, 0x00100000408], // paste + [0x00000000A3F, 0x00100000507], // find + [0x00000000A40, 0x00100000404], // cut + [0x00000000A41, 0x00100000508], // help + [0x00000000A44, 0x00100000C02], // browserFavorites + [0x00000000A46, 0x00100000A05], // mediaPlayPause + [0x00000000A48, 0x00100000A01], // close + [0x00000000003, 0x00100001002], // call + [0x00000000A4B, 0x00100000C05], // browserRefresh + [0x00000000A4C, 0x00100000D15], // exit + [0x00000000A51, 0x00100000409], // redo + [0x00000000A52, 0x00100000A01], // close + [0x00000000A55, 0x00100000A0C], // print + [0x00000000A58, 0x00100000504], // cancel + [0x00000000A5F, 0x00100000A0D], // save + [0x00000000A68, 0x00100000D25], // info + [0x00000000A6B, 0x00100000D47], // subtitle + [0x00000000A70, 0x00100000D49], // tv + [0x00000000A7E, 0x00100000D0C], // colorF0Red + [0x00000000A7F, 0x00100000D0D], // colorF1Green + [0x00000000A80, 0x00100000D0E], // colorF2Yellow + [0x00000000A81, 0x00100000D0F], // colorF3Blue + [0x00000000A82, 0x00100000D0B], // channelUp + [0x00000000A83, 0x00100000D0A], // channelDown + [0x00000000A8A, 0x0010000050D], // zoomIn + [0x00000000A8B, 0x0010000050E], // zoomOut + [0x00000000A98, 0x00100000A0E], // spellCheck + [0x00000000AF2, 0x0010000060B], // wakeUp + [0x00000000AFD, 0x00100000604], // eject + [0x00000000B00, 0x0010000080D], // f13 + [0x00000000B01, 0x0010000080E], // f14 + [0x00000000B02, 0x0010000080F], // f15 + [0x00000000B03, 0x00100000810], // f16 + [0x00000000B04, 0x00100000811], // f17 + [0x00000000B05, 0x00100000812], // f18 + [0x00000000B06, 0x00100000813], // f19 + [0x00000000B07, 0x00100000814], // f20 + [0x00000000B08, 0x00100000815], // f21 + [0x00000000B09, 0x00100000816], // f22 + [0x00000000B0A, 0x00100000817], // f23 + [0x00000000B0B, 0x00100000818], // f24 + [0x00000000B0F, 0x00200000000], // suspend + [0x00000000B12, 0x0000000003F], // question + [0x00000000811, 0x00000000040], // at + [0x00000000006, 0x00100001007], // headsetHook + [0x00000000017, 0x00100000A11], // audioVolumeMute + [0x00000000004, 0x00100001004] // endCall + ]); + /** Map OH KeyCode to Flutter PhysicalKey. + * Should map OH ScanCode to Flutter PhysicalKey, but we use KeyCode here + * instead since there is no ScanCode in OH KeyEvent yet. There may be some + * mistakes and should correct the map as soon as we can access ScanCode. + */ + public static toPhysicalKey: Map = + new Map([ + [0x000000007D0, 0x00000070027], // digit0 + [0x000000007D1, 0x0000007001E], // digit1 + [0x000000007D2, 0x0000007001F], // digit2 + [0x000000007D3, 0x00000070020], // digit3 + [0x000000007D4, 0x00000070021], // digit4 + [0x000000007D5, 0x00000070022], // digit5 + [0x000000007D6, 0x00000070023], // digit6 + [0x000000007D7, 0x00000070024], // digit7 + [0x000000007D8, 0x00000070025], // digit8 + [0x000000007D9, 0x00000070026], // digit9 + [0x000000007DC, 0x00000070052], // arrowUp + [0x000000007DD, 0x00000070051], // arrowDown + [0x000000007DE, 0x00000070050], // arrowLeft + [0x000000007DF, 0x0000007004F], // arrowRight + [0x000000007E1, 0x00000070004], // keyA + [0x000000007E2, 0x00000070005], // keyB + [0x000000007E3, 0x00000070006], // keyC + [0x000000007E4, 0x00000070007], // keyD + [0x000000007E5, 0x00000070008], // keyE + [0x000000007E6, 0x00000070009], // keyF + [0x000000007E7, 0x0000007000A], // keyG + [0x000000007E8, 0x0000007000B], // keyH + [0x000000007E9, 0x0000007000C], // keyI + [0x000000007EA, 0x0000007000D], // keyJ + [0x000000007EB, 0x0000007000E], // keyK + [0x000000007EC, 0x0000007000F], // keyL + [0x000000007ED, 0x00000070010], // keyM + [0x000000007EE, 0x00000070011], // keyN + [0x000000007EF, 0x00000070012], // keyO + [0x000000007F0, 0x00000070013], // keyP + [0x000000007F1, 0x00000070014], // keyQ + [0x000000007F2, 0x00000070015], // keyR + [0x000000007F3, 0x00000070016], // keyS + [0x000000007F4, 0x00000070017], // keyT + [0x000000007F5, 0x00000070018], // keyU + [0x000000007F6, 0x00000070019], // keyV + [0x000000007F7, 0x0000007001A], // keyW + [0x000000007F8, 0x0000007001B], // keyX + [0x000000007F9, 0x0000007001C], // keyY + [0x000000007FA, 0x0000007001D], // keyZ + [0x000000007FB, 0x00000070036], // comma + [0x000000007FC, 0x00000070037], // period + [0x000000007FD, 0x000000700E2], // altLeft + [0x000000007FE, 0x000000700E6], // altRight + [0x000000007FF, 0x000000700E1], // shiftLeft + [0x00000000800, 0x000000700E5], // shiftRight + [0x00000000801, 0x0000007002B], // tab + [0x00000000802, 0x0000007002C], // space + [0x00000000805, 0x000000C018A], // launchMail + [0x00000000806, 0x00000070028], // enter + [0x00000000807, 0x0000007002A], // backspace + [0x00000000808, 0x00000070035], // backquote + [0x00000000809, 0x0000007002D], // minus + [0x0000000080A, 0x0000007002E], // equal + [0x0000000080B, 0x0000007002F], // bracketLeft + [0x0000000080C, 0x00000070030], // bracketRight + [0x0000000080D, 0x00000070031], // backslash + [0x0000000080E, 0x00000070033], // semicolon + [0x0000000080F, 0x00000070034], // apostrophe/quote + [0x00000000810, 0x00000070038], // slash + [0x00000000813, 0x00000070065], // contextMenu + [0x00000000814, 0x0000007004B], // pageUp + [0x00000000815, 0x0000007004E], // pageDown + [0x00000000816, 0x00000070029], // escape + [0x00000000817, 0x0000007004C], // delete + [0x00000000818, 0x000000700E0], // controlLeft + [0x00000000819, 0x000000700E4], // controlRight + [0x0000000081A, 0x00000070039], // capsLock + [0x0000000081B, 0x00000070047], // scrollLock + [0x0000000081C, 0x000000700E3], // metaLeft + [0x0000000081D, 0x000000700E7], // metaRight + [0x0000000081E, 0x00000000012], // fn + [0x0000000081F, 0x00000070046], // printScreen + [0x00000000820, 0x00000070048], // pause + [0x00000000821, 0x0000007004A], // home + [0x00000000822, 0x0000007004D], // end + [0x00000000823, 0x00000070049], // insert + [0x00000000824, 0x000000C0225], // browserForward + [0x00000000825, 0x000000C00B0], // mediaPlay + [0x00000000826, 0x000000C00B1], // mediaPause + [0x00000000828, 0x000000C00B8], // eject + [0x00000000829, 0x000000C00B2], // mediaRecord + [0x0000000082A, 0x0000007003A], // f1 + [0x0000000082B, 0x0000007003B], // f2 + [0x0000000082C, 0x0000007003C], // f3 + [0x0000000082D, 0x0000007003D], // f4 + [0x0000000082E, 0x0000007003E], // f5 + [0x0000000082F, 0x0000007003F], // f6 + [0x00000000830, 0x00000070040], // f7 + [0x00000000831, 0x00000070041], // f8 + [0x00000000832, 0x00000070042], // f9 + [0x00000000833, 0x00000070043], // f10 + [0x00000000834, 0x00000070044], // f11 + [0x00000000835, 0x00000070045], // f12 + [0x00000000836, 0x00000070053], // numLock + [0x00000000837, 0x00000070062], // numpad0 + [0x00000000838, 0x00000070059], // numpad1 + [0x00000000839, 0x0000007005A], // numpad2 + [0x0000000083A, 0x0000007005B], // numpad3 + [0x0000000083B, 0x0000007005C], // numpad4 + [0x0000000083C, 0x0000007005D], // numpad5 + [0x0000000083D, 0x0000007005E], // numpad6 + [0x0000000083E, 0x0000007005F], // numpad7 + [0x0000000083F, 0x00000070060], // numpad8 + [0x00000000840, 0x00000070061], // numpad9 + [0x00000000841, 0x00000070054], // numpadDivide + [0x00000000842, 0x00000070055], // numpadMultiply + [0x00000000843, 0x00000070056], // numpadSubtract + [0x00000000844, 0x00000070057], // numpadAdd + [0x00000000845, 0x00000070063], // numpadDecimal + [0x00000000846, 0x00000070085], // numpadComma + [0x00000000847, 0x00000070058], // numpadEnter + [0x00000000848, 0x00000070067], // numpadEqual + [0x00000000849, 0x000000700B6], // numpadParenLeft + [0x0000000084A, 0x000000700B7], // numpadParenRight + [0x00000000010, 0x00000070080], // audioVolumeUp + [0x00000000011, 0x00000070081], // audioVolumeDown + [0x00000000012, 0x00000070066], // power + [0x00000000001, 0x0000007004A], // home + [0x00000000028, 0x000000C006F], // brightnessUp + [0x00000000029, 0x000000C0070], // brightnessDown + [0x0000000000A, 0x000000C00CD], // mediaPlayPause + [0x0000000000B, 0x000000C00B7], // mediaStop + [0x0000000000C, 0x000000C00B5], // mediaTrackNext + [0x0000000000D, 0x000000C00B6], // mediaTrackPrevious + [0x0000000000E, 0x000000C00B4], // mediaRewind + [0x0000000000F, 0x000000C00B3], // mediaFastForward + [0x00000000A28, 0x00000010082], // sleep + [0x00000000A2E, 0x0000007008A], // convert + [0x00000000A30, 0x0000007008B], // nonConvert + [0x00000000A37, 0x00000070089], // intlYen + [0x00000000A39, 0x00000070079], // again + [0x00000000A3A, 0x000000700A3], // props + [0x00000000A3B, 0x0000007007A], // undo + [0x00000000A3C, 0x0000007007C], // copy + [0x00000000A3D, 0x00000070074], // open + [0x00000000A3E, 0x0000007007D], // paste + [0x00000000A3F, 0x0000007007E], // find + [0x00000000A40, 0x0000007007B], // cut + [0x00000000A41, 0x00000070075], // help + [0x00000000A44, 0x000000C022A], // browserFavorites + [0x00000000A46, 0x000000C00CD], // mediaPlayPause + [0x00000000A48, 0x000000C0203], // close + [0x00000000A4B, 0x000000C0227], // browserRefresh + [0x00000000A4C, 0x000000C0094], // exit + [0x00000000A51, 0x000000C0279], // redo + [0x00000000A52, 0x000000C0203], // close + [0x00000000A55, 0x000000C0208], // print + [0x00000000A5F, 0x000000C0207], // save + [0x00000000A68, 0x000000C0060], // info + [0x00000000A82, 0x000000C009C], // channelUp + [0x00000000A83, 0x000000C009D], // channelDown + [0x00000000A8A, 0x000000C022D], // zoomIn + [0x00000000A8B, 0x000000C022E], // zoomOut + [0x00000000A98, 0x000000C01AB], // spellCheck + [0x00000000AF2, 0x00000010083], // wakeUp + [0x00000000AFD, 0x000000C00B8], // eject + [0x00000000B00, 0x00000070068], // f13 + [0x00000000B01, 0x00000070069], // f14 + [0x00000000B02, 0x0000007006A], // f15 + [0x00000000B03, 0x0000007006B], // f16 + [0x00000000B04, 0x0000007006C], // f17 + [0x00000000B05, 0x0000007006D], // f18 + [0x00000000B06, 0x0000007006E], // f19 + [0x00000000B07, 0x0000007006F], // f20 + [0x00000000B08, 0x00000070070], // f21 + [0x00000000B09, 0x00000070071], // f22 + [0x00000000B0A, 0x00000070072], // f23 + [0x00000000B0B, 0x00000070073], // f24 + [0x00000000B0F, 0x00000000014], // suspend + [0x00000000017, 0x0000007007F] // audioVolumeMute + ]); + /* Map Oh KeyCode to ScanCode since cannot access ScanCode directly from KeyEvent */ + public static ohKeyToScanCode: Map = + new Map([ + [0x000000007D0, 0x0000000000B], // digit0 + [0x000000007D1, 0x00000000002], // digit1 + [0x000000007D2, 0x00000000003], // digit2 + [0x000000007D3, 0x00000000004], // digit3 + [0x000000007D4, 0x00000000005], // digit4 + [0x000000007D5, 0x00000000006], // digit5 + [0x000000007D6, 0x00000000007], // digit6 + [0x000000007D7, 0x00000000008], // digit7 + [0x000000007D8, 0x00000000009], // digit8 + [0x000000007D9, 0x0000000000A], // digit9 + [0x000000007DC, 0x00000000067], // arrowUp + [0x000000007DD, 0x0000000006C], // arrowDown + [0x000000007DE, 0x00000000069], // arrowLeft + [0x000000007DF, 0x0000000006A], // arrowRight + [0x000000007E1, 0x0000000001E], // keyA + [0x000000007E2, 0x00000000030], // keyB + [0x000000007E3, 0x0000000002E], // keyC + [0x000000007E4, 0x00000000020], // keyD + [0x000000007E5, 0x00000000012], // keyE + [0x000000007E6, 0x00000000021], // keyF + [0x000000007E7, 0x00000000022], // keyG + [0x000000007E8, 0x00000000023], // keyH + [0x000000007E9, 0x00000000017], // keyI + [0x000000007EA, 0x00000000024], // keyJ + [0x000000007EB, 0x00000000025], // keyK + [0x000000007EC, 0x00000000026], // keyL + [0x000000007ED, 0x00000000032], // keyM + [0x000000007EE, 0x00000000031], // keyN + [0x000000007EF, 0x00000000018], // keyO + [0x000000007F0, 0x00000000019], // keyP + [0x000000007F1, 0x00000000010], // keyQ + [0x000000007F2, 0x00000000013], // keyR + [0x000000007F3, 0x0000000001F], // keyS + [0x000000007F4, 0x00000000014], // keyT + [0x000000007F5, 0x00000000016], // keyU + [0x000000007F6, 0x0000000002F], // keyV + [0x000000007F7, 0x00000000011], // keyW + [0x000000007F8, 0x0000000002D], // keyX + [0x000000007F9, 0x00000000015], // keyY + [0x000000007FA, 0x0000000002C], // keyZ + [0x000000007FB, 0x00000000033], // comma + [0x000000007FC, 0x00000000034], // period + [0x000000007FD, 0x00000000038], // altLeft + [0x000000007FE, 0x00000000064], // altRight + [0x000000007FF, 0x0000000002A], // shiftLeft + [0x00000000800, 0x00000000036], // shiftRight + [0x00000000801, 0x0000000000F], // tab + [0x00000000802, 0x00000000039], // space + [0x00000000805, 0x000000000D7], // launchMail + [0x00000000806, 0x0000000001C], // enter + [0x00000000807, 0x0000000000E], // backspace + [0x00000000808, 0x00000000029], // backquote + [0x00000000809, 0x0000000000C], // minus + [0x0000000080A, 0x0000000000D], // equal + [0x0000000080B, 0x0000000001A], // bracketLeft + [0x0000000080C, 0x0000000001B], // bracketRight + [0x0000000080D, 0x00000000056], // backslash + [0x0000000080E, 0x00000000027], // semicolon + [0x0000000080F, 0x00000000028], // apostrophe/quote + [0x00000000810, 0x00000000035], // slash + [0x00000000813, 0x0000000008B], // contextMenu + [0x00000000814, 0x000000000B1], // pageUp + [0x00000000815, 0x000000000B2], // pageDown + [0x00000000816, 0x00000000001], // escape + [0x00000000817, 0x0000000006F], // delete + [0x00000000818, 0x0000000001D], // controlLeft + [0x00000000819, 0x00000000061], // controlRight + [0x0000000081A, 0x0000000003A], // capsLock + [0x0000000081B, 0x00000000046], // scrollLock + [0x0000000081C, 0x0000000007D], // metaLeft + [0x0000000081D, 0x0000000007E], // metaRight + [0x0000000081E, 0x000000001D0], // fn + [0x0000000081F, 0x00000000063], // printScreen + [0x00000000820, 0x0000000019B], // pause + [0x00000000821, 0x00000000066], // home + [0x00000000822, 0x0000000006B], // end + [0x00000000823, 0x0000000006E], // insert + [0x00000000824, 0x0000000009F], // browserForward + [0x00000000825, 0x000000000CF], // mediaPlay + [0x00000000826, 0x000000000C9], // mediaPause + [0x00000000828, 0x000000000A2], // eject + [0x00000000829, 0x000000000A7], // mediaRecord + [0x0000000082A, 0x0000000003B], // f1 + [0x0000000082B, 0x0000000003C], // f2 + [0x0000000082C, 0x0000000003D], // f3 + [0x0000000082D, 0x0000000003E], // f4 + [0x0000000082E, 0x0000000003F], // f5 + [0x0000000082F, 0x00000000040], // f6 + [0x00000000830, 0x00000000041], // f7 + [0x00000000831, 0x00000000042], // f8 + [0x00000000832, 0x00000000043], // f9 + [0x00000000833, 0x00000000044], // f10 + [0x00000000834, 0x00000000057], // f11 + [0x00000000835, 0x00000000058], // f12 + [0x00000000836, 0x00000000045], // numLock + [0x00000000837, 0x00000000052], // numpad0 + [0x00000000838, 0x0000000004F], // numpad1 + [0x00000000839, 0x00000000050], // numpad2 + [0x0000000083A, 0x00000000051], // numpad3 + [0x0000000083B, 0x0000000004B], // numpad4 + [0x0000000083C, 0x0000000004C], // numpad5 + [0x0000000083D, 0x0000000004D], // numpad6 + [0x0000000083E, 0x00000000047], // numpad7 + [0x0000000083F, 0x00000000048], // numpad8 + [0x00000000840, 0x00000000049], // numpad9 + [0x00000000841, 0x00000000062], // numpadDivide + [0x00000000842, 0x00000000037], // numpadMultiply + [0x00000000843, 0x0000000004A], // numpadSubtract + [0x00000000844, 0x0000000004E], // numpadAdd + [0x00000000845, 0x00000000053], // numpadDecimal + [0x00000000846, 0x00000000079], // numpadComma + [0x00000000847, 0x00000000060], // numpadEnter + [0x00000000848, 0x00000000075], // numpadEqual + [0x00000000849, 0x000000000B3], // numpadParenLeft + [0x0000000084A, 0x000000000B4], // numpadParenRight + [0x00000000010, 0x00000000073], // audioVolumeUp + [0x00000000011, 0x00000000072], // audioVolumeDown + [0x00000000012, 0x00000000098], // power + [0x00000000001, 0x00000000066], // home + [0x00000000028, 0x000000000E1], // brightnessUp + [0x00000000029, 0x000000000E0], // brightnessDown + [0x0000000000A, 0x000000000A4], // mediaPlayPause + [0x0000000000B, 0x000000000A6], // mediaStop + [0x0000000000C, 0x000000000A3], // mediaTrackNext + [0x0000000000D, 0x000000000A5], // mediaTrackPrevious + [0x0000000000E, 0x000000000A8], // mediaRewind + [0x0000000000F, 0x000000000D0], // mediaFastForward + [0x00000000A28, 0x0000000008E], // sleep + [0x00000000A2E, 0x0000000005C], // convert + [0x00000000A30, 0x0000000005E], // nonConvert + [0x00000000A37, 0x0000000007C], // intlYen + [0x00000000A39, 0x00000000081], // again + [0x00000000A3A, 0x00000000082], // props + [0x00000000A3B, 0x00000000083], // undo + [0x00000000A3C, 0x00000000085], // copy + [0x00000000A3D, 0x00000000086], // open + [0x00000000A3E, 0x00000000087], // paste + [0x00000000A3F, 0x00000000088], // find + [0x00000000A40, 0x00000000089], // cut + [0x00000000A41, 0x0000000008A], // help + [0x00000000A44, 0x0000000009C], // browserFavorites + [0x00000000A46, 0x000000000A4], // mediaPlayPause + [0x00000000A48, 0x000000000CE], // close + [0x00000000A4C, 0x000000000AE], // exit + [0x00000000A51, 0x000000000B6], // redo + [0x00000000A52, 0x000000000CE], // close + [0x00000000A55, 0x000000000D2], // print + [0x00000000A68, 0x00000000166], // info + [0x00000000A82, 0x00000000192], // channelUp + [0x00000000A83, 0x00000000193], // channelDown + [0x00000000AF2, 0x0000000008F], // wakeUp + [0x00000000AFD, 0x000000000A2], // eject + [0x00000000B00, 0x000000000B7], // f13 + [0x00000000B01, 0x000000000B8], // f14 + [0x00000000B02, 0x000000000B9], // f15 + [0x00000000B03, 0x000000000BA], // f16 + [0x00000000B04, 0x000000000BB], // f17 + [0x00000000B05, 0x000000000BC], // f18 + [0x00000000B06, 0x000000000BD], // f19 + [0x00000000B07, 0x000000000BE], // f20 + [0x00000000B08, 0x000000000BF], // f21 + [0x00000000B09, 0x000000000C0], // f22 + [0x00000000B0A, 0x000000000C1], // f23 + [0x00000000B0B, 0x000000000C2], // f24 + [0x00000000B0F, 0x000000000CD], // suspend + [0x00000000017, 0x00000000071], // audioVolumeMute + ]); + public static kValueMask: number = 0x000FFFFFFFF; + public static kUnicodePlane: number = 0x00000000000; + public static kOhosPlane: number = 0x01900000000; + public static modifierGoals: ModifierGoal[] = [ + new ModifierGoal( + "Ctrl", + [ + new KeyPair(0x000700e0, 0x0200000100), // CtrlLeft + new KeyPair(0x000700e4, 0x0200000101) // CtrlRight + ] + ), + new ModifierGoal( + "Shift", + [ + new KeyPair(0x000700e1, 0x0200000102), // ShiftLeft + new KeyPair(0x000700e5, 0x0200000103) // SHIftRight + ] + ), + new ModifierGoal( + "Alt", + [ + new KeyPair(0x000700e2, 0x0200000104), // AltLeft + new KeyPair(0x000700e6, 0x0200000105) // AltRight + ] + ) + ]; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/OhosTouchProcessor.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/OhosTouchProcessor.ets new file mode 100644 index 0000000000000000000000000000000000000000..be166a24b126410ea797814e35226d923167ff28 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/OhosTouchProcessor.ets @@ -0,0 +1,57 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { TouchEvent } from '@ohos.multimodalInput.touchEvent'; +import Any from '../../plugin/common/Any'; + +export default class OhosTouchProcessor { + private static POINTER_DATA_FIELD_COUNT: number = 35; + static BYTES_PER_FIELD: number = 8; + private static POINTER_DATA_FLAG_BATCHED: number = 1; + + public onTouchEvent(event: TouchEvent, transformMatrix: Any): void { + + } +} + +export enum PointerChange { + CANCEL = 0, + ADD = 1, + REMOVE = 2, + HOVER = 3, + DOWN = 4, + MOVE = 5, + UP = 6, + PAN_ZOOM_START = 7, + PAN_ZOOM_UPDATE = 8, + PAN_ZOOM_END = 9 +} + +export enum PointerDeviceKind { + TOUCH = 0, + MOUSE = 1, + STYLUS = 2, + INVERTED_STYLUS = 3, + TRACKPAD = 4, + UNKNOWN = 5 +} + +export enum PointerSignalKind { + NONE = 0, + SCROLL = 1, + SCROLL_INERTIA_CANCEL = 2, + SCALE = 3, + UNKNOWN = 4 +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/PlatformViewInfo.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/PlatformViewInfo.ets new file mode 100644 index 0000000000000000000000000000000000000000..9ef3ce7273e99a936f8a76f30053211c93448029 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/PlatformViewInfo.ets @@ -0,0 +1,37 @@ +/* +* Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import PlatformView from '../../plugin/platform/PlatformView'; + +export class PlatformViewInfo { + public platformView: PlatformView; + public surfaceId: string; + public width: number; + public height: number; + public top: number; + public left: number; + public direction: Direction; + + constructor(platformView: PlatformView, surfaceId: string, width: number, height: number, top: number, left: number, + direction: Direction) { + this.platformView = platformView; + this.surfaceId = surfaceId; + this.width = width; + this.height = height; + this.top = top; + this.left = left; + this.direction = direction; + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/Settings.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/Settings.ets new file mode 100644 index 0000000000000000000000000000000000000000..945b85035b363ec79e3998fb783a35c3a6f02f80 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/Settings.ets @@ -0,0 +1,63 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import SettingsChannel, { PlatformBrightness } from '../engine/systemchannels/SettingsChannel' +import I18n from '@ohos.i18n' +import Log from '../../util/Log'; +import { MediaQuery } from '@ohos.arkui.UIContext'; + + +const TAG = "Settings"; + +export default class Settings { + settingsChannel: SettingsChannel | null; + + constructor(settingsChannel: SettingsChannel | null) { + this.settingsChannel = settingsChannel; + } + + sendSettings(mediaQuery: MediaQuery): void { + this.settingsChannel?.startMessage() + .setAlwaysUse24HourFormat(I18n.System.is24HourClock()) + .setNativeSpellCheckServiceDefined(false) + .setBrieflyShowPassword(false) + .setPlatformBrightness(this.getThemeMode(mediaQuery)) + .setTextScaleFactor(this.getTextScaleFactor()) + .send(); + } + + getThemeMode(mediaQuery: MediaQuery): PlatformBrightness { + + let listener = mediaQuery.matchMediaSync('(dark-mode: true)'); + if (listener.matches) { + Log.i(TAG, "return dark"); + return PlatformBrightness.DARK; + } else { + Log.i(TAG, "return light"); + return PlatformBrightness.LIGHT; + } + } + + getTextScaleFactor() : number { + let sysTextScaleFactor = AppStorage.get('fontSizeScale'); + if(sysTextScaleFactor == undefined) { + sysTextScaleFactor = 1.0; + Log.e(TAG, 'get textScaleFactor error, it is assigned to ' + JSON.stringify(sysTextScaleFactor)); + } + Log.i(TAG, "return textScaleFactor = " + JSON.stringify(sysTextScaleFactor)) + return sysTextScaleFactor; + } + +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/TouchEventProcessor.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/TouchEventProcessor.ets new file mode 100644 index 0000000000000000000000000000000000000000..798d588463f9ea116c839ffb3ca2082d35a0efbd --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/TouchEventProcessor.ets @@ -0,0 +1,288 @@ +/* +* Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ +/** Handle the motion events received by the FlutterNapi. */ +// import PlainArray from '@ohos.util.PlainArray'; +// import { TouchEvent } from '@ohos.multimodalInput.touchEvent'; +// import Queue from '@ohos.util.Queue'; + +import { CustomTouchEvent, CustomTouchObject } from '../../plugin/platform/CustomTouchEvent'; +import display from '@ohos.display'; +import FlutterManager from './FlutterManager'; +import { EmbeddingNodeController } from './EmbeddingNodeController'; +import Any from '../../plugin/common/Any'; + + +const OH_NATIVEXCOMPONENT_UNKNOWN = 4; +const OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN = 0; + +class OH_NativeXComponent_TouchPoint { + id: number = 0; + screenX: number = 0.0; + screenY: number = 0.0; + x: number = 0.0; + y: number = 0.0; + type: number = OH_NATIVEXCOMPONENT_UNKNOWN; + size: number = 0; + force: number = 0; + timeStamp: number = 0; + isPressed: boolean = false; + + constructor(id: number, + screenX: number, + screenY: number, + x: number, + y: number, + type: number, + size: number, + force: number, + timeStamp: number, + isPressed: boolean) { + this.id = id; + this.screenX = screenX; + this.screenY = screenY; + this.x = x; + this.y = y; + this.type = type; + this.size = size; + this.force = force; + this.timeStamp = timeStamp; + this.isPressed = isPressed; + } +} + +class OH_NativeXComponent_TouchEvent { + id: number = 0; + screenX: number = 0.0; + screenY: number = 0.0; + x: number = 0.0; + y: number = 0.0; + type: number = OH_NATIVEXCOMPONENT_UNKNOWN; + size: number = 0; + force: number = 0; + deviceId: number = 0; + timeStamp: number = 0; + touchPoints: OH_NativeXComponent_TouchPoint[] = []; + numPoints: number = 0; + + constructor(id: number, + screenX: number, + screenY: number, + x: number, + y: number, + type: number, + size: number, + force: number, + deviceId: number, + timeStamp: number, + touchPoints: OH_NativeXComponent_TouchPoint[], + numPoints: number) { + this.id = id; + this.screenX = screenX; + this.screenY = screenY; + this.x = x; + this.y = y; + this.type = type; + this.size = size; + this.force = force; + this.deviceId = deviceId; + this.timeStamp = timeStamp; + this.touchPoints = touchPoints; + this.numPoints = numPoints; + } +} + +class TouchPacket { + touchEvent: OH_NativeXComponent_TouchEvent; + toolType: number = OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN; + tiltX: number = 0; + tiltY: number = 0; + + constructor(touchEvent: OH_NativeXComponent_TouchEvent, + toolType: number, + tiltX: number, + tiltY: number) { + this.touchEvent = touchEvent; + this.toolType = toolType; + this.tiltX = tiltX; + this.tiltY = tiltY; + } +} + +export default class TouchEventProcessor { + private static instance: TouchEventProcessor; + + static getInstance(): TouchEventProcessor { + if (TouchEventProcessor.instance == null) { + TouchEventProcessor.instance = new TouchEventProcessor(); + } + return TouchEventProcessor.instance; + } + + private decodeTouchPacket(strings: Array, densityPixels: number, top: number, left: number): TouchPacket { + let offset: number = 0; + let numPoint: number = parseInt(strings[offset++]); + let changesId: number = parseInt(strings[offset++]); + let changesscreenX: number = (parseFloat(strings[offset++]) / densityPixels); + let changesscreenY: number = (parseFloat(strings[offset++]) / densityPixels); + let changesX: number = ((parseFloat(strings[offset++]) / densityPixels) - left); + let changesY: number = ((parseFloat(strings[offset++]) / densityPixels) - top); + let changesType: number = parseInt(strings[offset++]); + let changesSize: number = parseFloat(strings[offset++]); + let changesForce: number = parseFloat(strings[offset++]); + let changesDeviceId: number = parseInt(strings[offset++]); + let changesTimeStamp: number = parseInt(strings[offset++]); + + const touchPoints: OH_NativeXComponent_TouchPoint[] = []; + for (let i = 0; i < numPoint; i++) { + const touchPoint: OH_NativeXComponent_TouchPoint = new OH_NativeXComponent_TouchPoint( + parseInt(strings[offset++]), + (parseFloat(strings[offset++]) / densityPixels), + (parseFloat(strings[offset++]) / densityPixels), + ((parseFloat(strings[offset++]) / densityPixels) - left), + ((parseFloat(strings[offset++]) / densityPixels) - top), + parseInt(strings[offset++]), + parseFloat(strings[offset++]), + parseFloat(strings[offset++]), + parseInt(strings[offset++]), + parseInt(strings[offset++]) === 1 ? true : false + ); + touchPoints.push(touchPoint); + } + + const touchEventInput: OH_NativeXComponent_TouchEvent = new OH_NativeXComponent_TouchEvent( + changesId, + changesscreenX, + changesscreenY, + changesX, + changesY, + changesType, + changesSize, + changesForce, + changesDeviceId, + changesTimeStamp, + touchPoints, + numPoint + ); + + let toolTypeInput: number = parseInt(strings[offset++]); + let tiltXTouch: number = parseInt(strings[offset++]); + let tiltYTouch: number = parseInt(strings[offset++]); + + const touchPointEventPacket: TouchPacket = new TouchPacket( + touchEventInput, + toolTypeInput, + tiltXTouch, + tiltYTouch + ); + return touchPointEventPacket; + } + + private constructCustomTouchEventImpl(touchPacket: TouchPacket): CustomTouchEvent { + let changes1: CustomTouchObject = new CustomTouchObject( + touchPacket.touchEvent.type, + touchPacket.touchEvent.id, + touchPacket.touchEvent.screenX, + touchPacket.touchEvent.screenY, + touchPacket.touchEvent.screenX, + touchPacket.touchEvent.screenY, + touchPacket.touchEvent.screenX, + touchPacket.touchEvent.screenY, + touchPacket.touchEvent.x, + touchPacket.touchEvent.y + ); + + let touches: CustomTouchObject[] = []; + let touchPointer: number = touchPacket.touchEvent.numPoints; + for (let i = 0; i < touchPointer; i++) { + let touchesItem: CustomTouchObject = new CustomTouchObject( + touchPacket.touchEvent.touchPoints[i].type, + touchPacket.touchEvent.touchPoints[i].id, + touchPacket.touchEvent.touchPoints[i].screenX, + touchPacket.touchEvent.touchPoints[i].screenY, + touchPacket.touchEvent.touchPoints[i].screenX, + touchPacket.touchEvent.touchPoints[i].screenY, + touchPacket.touchEvent.touchPoints[i].screenX, + touchPacket.touchEvent.touchPoints[i].screenY, + touchPacket.touchEvent.touchPoints[i].x, + touchPacket.touchEvent.touchPoints[i].y + ); + touches.push(touchesItem); + } + + let customTouchEvent1: CustomTouchEvent = new CustomTouchEvent( + touchPacket.touchEvent.type, + touches, + [changes1], + touchPacket.touchEvent.timeStamp, + SourceType.TouchScreen, + touchPacket.touchEvent.force, + touchPacket.tiltX, + touchPacket.tiltY, + touchPacket.toolType + ); + + return customTouchEvent1; + } + + /** Construct the CustomTouchEvent and return. */ + public constructCustomTouchEvent(strings: Array, top: number, left: number): CustomTouchEvent { + let densityPixels: number = display.getDefaultDisplaySync().densityPixels; + + let touchPacket: TouchPacket = this.decodeTouchPacket(strings, densityPixels, top, left); + let customTouchEvent: CustomTouchEvent = this.constructCustomTouchEventImpl(touchPacket); + return customTouchEvent; + } + + public postTouchEvent(strings: Array) { + FlutterManager.getInstance().getFlutterViewList().forEach((value) => { + let length = value.getDVModel().children.length + for (let index = length - 1; index >= 0; index--) { + let dvModel = value.getDVModel().children[index] + let params = dvModel.getLayoutParams() as Record; + let left = params['left'] as number ?? 0; + let top = params['top'] as number ?? 0; + let down = params['down'] as boolean ?? false; + if (down) { + //如果flutter端判断当前platformView是可点击的,则将事件分发出去 + let touchEvent: CustomTouchEvent = TouchEventProcessor.getInstance().constructCustomTouchEvent(strings, top, left); + let nodeController = params['nodeController'] as EmbeddingNodeController; + nodeController.postEvent(touchEvent) + } else { + //如果触摸事件为OH_NATIVEXCOMPONENT_DOWN=0,且只有一个手指,说明是下一次点击了,这时候需要清空上一次的数据 + if (strings[6] == '0' && strings[0] == '1') { + params['touchEvent'] = undefined + } + //如果触摸事件为OH_NATIVEXCOMPONENT_DOWN=0类型,且在flutter端还没判断当前view是否处于点击区域内,则 + //将点击事件存储在list列表中。 + let touchEvent: CustomTouchEvent = TouchEventProcessor.getInstance().constructCustomTouchEvent(strings, top, left); + let array: Array | undefined = params['touchEvent'] as Array + if (array == undefined) { + array = [] + params['touchEvent'] = array + } + array.push(touchEvent) + } + } + }); + } + + public checkHitPlatformView(left: number, top: number, width: number, height: number, x: number, y: number): boolean { + if (x >= left && x <= (left + width) && y >= top && y <= (top + height)) { + return true; + } else { + return false; + } + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/TouchEventTracker.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/TouchEventTracker.ets new file mode 100644 index 0000000000000000000000000000000000000000..9ff8b1f79cba577dd9d7384accfdccceb75739b5 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/TouchEventTracker.ets @@ -0,0 +1,87 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ +/** Tracks the motion events received by the FlutterView. */ +import PlainArray from '@ohos.util.PlainArray'; +import { TouchEvent } from '@ohos.multimodalInput.touchEvent'; +import Queue from '@ohos.util.Queue'; + +export class TouchEventTracker { + private eventById: PlainArray; + private unusedEvents: Queue; + private static INSTANCE: TouchEventTracker; + + public static getInstance(): TouchEventTracker { + if (TouchEventTracker.INSTANCE == null) { + TouchEventTracker.INSTANCE = new TouchEventTracker(); + } + return TouchEventTracker.INSTANCE; + } + + constructor() { + this.eventById = new PlainArray(); + this.unusedEvents = new Queue(); + } + + /** Tracks the event and returns a unique MotionEventId identifying the event. */ + public track(event: TouchEvent): TouchEventId { + const eventId: TouchEventId = TouchEventId.createUnique(); + this.eventById.add(eventId.getId(), event); + this.unusedEvents.add(eventId.getId()); + return eventId; + } + + /** + * Returns the MotionEvent corresponding to the eventId while discarding all the motion events + * that occurred prior to the event represented by the eventId. Returns null if this event was + * popped or discarded. + */ + public pop(eventId: TouchEventId): TouchEvent { + // remove all the older events. + while (this.unusedEvents.length != 0 && this.unusedEvents.getFirst() < eventId.getId()) { + this.eventById.remove(this.unusedEvents.pop()); + } + + // remove the current event from the heap if it exists. + if (this.unusedEvents.length != 0 && this.unusedEvents.getFirst() == eventId.getId()) { + this.unusedEvents.pop(); + } + + const event: TouchEvent = this.eventById.get(eventId.getId()); + this.eventById.remove(eventId.getId()); + return event; + } +} + +/** Represents a unique identifier corresponding to a motion event. */ +export class TouchEventId { + private static ID_COUNTER: number = 0; + private id: number; + + constructor(id: number) { + this.id = id; + } + + public static from(id: number): TouchEventId { + return new TouchEventId(id); + } + + public static createUnique(): TouchEventId { + return new TouchEventId(TouchEventId.ID_COUNTER++); + } + + public getId(): number { + return this.id; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/WindowInfoRepositoryCallbackAdapterWrapper.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/WindowInfoRepositoryCallbackAdapterWrapper.ets new file mode 100644 index 0000000000000000000000000000000000000000..67faee088cb4a6db9ca59dfaaf3823d28c2143bf --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/WindowInfoRepositoryCallbackAdapterWrapper.ets @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import UIAbility from '@ohos.app.ability.UIAbility'; + +export default class WindowInfoRepositoryCallbackAdapterWrapper { + constructor() { + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/PlatformPlugin.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/PlatformPlugin.ets new file mode 100644 index 0000000000000000000000000000000000000000..16ef44a2c1155402ce9da6f029b8f2a999829fa2 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/PlatformPlugin.ets @@ -0,0 +1,406 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; +import { BusinessError } from '@kit.BasicServicesKit'; +import PlatformChannel, { + AppSwitcherDescription, + Brightness, + ClipboardContentFormat, + HapticFeedbackType, + PlatformMessageHandler, + SoundType, + SystemChromeStyle, + SystemUiMode, + SystemUiOverlay +} from '../embedding/engine/systemchannels/PlatformChannel'; +import FlutterManager from '../embedding/ohos/FlutterManager'; +import pasteboard from '@ohos.pasteboard'; +import Log from '../util/Log'; +import vibrator from '@ohos.vibrator'; +import window from '@ohos.window'; +import common from '@ohos.app.ability.common'; +import { MethodResult } from './common/MethodChannel'; +import Any from './common/Any'; +import router from '@ohos.router'; + +/** + * ohos实现platform plugin + */ +export default class PlatformPlugin { + private static TAG = "PlatformPlugin"; + callback = new PlatformPluginCallback(); + + constructor(platformChannel: PlatformChannel, context: common.Context, + platformPluginDelegate?: PlatformPluginDelegate) { + this.callback.platformChannel = platformChannel; + this.callback.context = context; + this.callback.applicationContext = context?.getApplicationContext(); + this.callback.platform = this; + this.callback.platformPluginDelegate = platformPluginDelegate ?? null; + this.callback.platformChannel?.setPlatformMessageHandler(this.callback); + } + + initWindow() { + try { + let context = this.callback.context!! + window.getLastWindow(context, (err, data) => { + if (err.code) { + Log.e(PlatformPlugin.TAG, "Failed to obtain the top window. Cause: " + JSON.stringify(err)); + return; + } + this.callback.lastWindow = data; + }); + const uiAbility = FlutterManager.getInstance().getUIAbility(context); + const windowStage = FlutterManager.getInstance().getWindowStage(uiAbility); + this.callback.mainWindow = windowStage.getMainWindowSync(); + } catch (err) { + Log.e(PlatformPlugin.TAG, "Failed to obtain the top window. Cause: " + JSON.stringify(err)); + } + } + + + updateSystemUiOverlays(): void { + this.callback.mainWindow?.setWindowSystemBarEnable(this.callback.showBarOrNavigation); + if (this.callback.currentTheme != null) { + this.callback.setSystemChromeSystemUIOverlayStyle(this.callback.currentTheme); + } + } + + setUIAbilityContext(context: common.UIAbilityContext): void { + this.callback.uiAbilityContext = context; + } + + setSystemChromeChangeListener(): void { + if (this.callback.callbackId == null && this.callback.applicationContext != null) { + let that = this; + this.callback.callbackId = this.callback.applicationContext?.on('environment', { + onConfigurationUpdated(config) { + Log.d(PlatformPlugin.TAG, "onConfigurationUpdated: " + that.callback.showBarOrNavigation); + that.callback.platformChannel?.systemChromeChanged(that.callback.showBarOrNavigation.includes('status')); + }, + onMemoryLevel(level) { + } + }) + } + } + + public destroy() { + this.callback.platformChannel?.setPlatformMessageHandler(null); + } +} + +export interface PlatformPluginDelegate { + popSystemNavigator(): boolean; +} + +export class PlatformPluginCallback implements PlatformMessageHandler { + private static TAG = "PlatformPluginCallback"; + platform: PlatformPlugin | null = null; + mainWindow: window.Window | null = null; + lastWindow: window.Window | null = null; + platformChannel: PlatformChannel | null = null; + platformPluginDelegate: PlatformPluginDelegate | null = null; + context: common.Context | null = null; + showBarOrNavigation: ('status' | 'navigation')[] = ['status', 'navigation']; + uiAbilityContext: common.UIAbilityContext | null = null; + callbackId: number | null = null; + applicationContext: common.ApplicationContext | null = null; + currentTheme: SystemChromeStyle | null = null; + + playSystemSound(soundType: SoundType) { + } + + async vibrateHapticFeedback(feedbackType: HapticFeedbackType) { + switch (feedbackType) { + case HapticFeedbackType.STANDARD: + await vibrator.startVibration({ type: 'time', duration: 75 }, + { id: 0, usage: 'touch' }); + break; + case HapticFeedbackType.LIGHT_IMPACT: + await vibrator.startVibration({ type: 'time', duration: 25 }, + { id: 0, usage: 'touch' }); + break; + case HapticFeedbackType.MEDIUM_IMPACT: + await vibrator.startVibration({ type: 'time', duration: 150 }, + { id: 0, usage: 'touch' }); + break; + case HapticFeedbackType.HEAVY_IMPACT: + await vibrator.startVibration({ type: 'time', duration: 300 }, + { id: 0, usage: 'touch' }); + break; + case HapticFeedbackType.SELECTION_CLICK: + await vibrator.startVibration({ type: 'time', duration: 100 }, + { id: 0, usage: 'touch' }); + break; + } + } + + setPreferredOrientations(ohosOrientation: number, result: MethodResult) { + try { + Log.d(PlatformPluginCallback.TAG, "ohosOrientation: " + ohosOrientation); + this.mainWindow!.setPreferredOrientation(ohosOrientation, (err: BusinessError) => { + const errCode: number = err.code; + if (errCode) { + Log.e(PlatformPluginCallback.TAG, "Failed to set window orientation:" + JSON.stringify(err)); + result.error("error", JSON.stringify(err), null); + return; + } + result.success(null); + }); + } catch (exception) { + Log.e(PlatformPluginCallback.TAG, "Failed to set window orientation:" + JSON.stringify(exception)); + result.error("error", JSON.stringify(exception), null); + } + } + + setApplicationSwitcherDescription(description: AppSwitcherDescription) { + Log.d(PlatformPluginCallback.TAG, "setApplicationSwitcherDescription: " + JSON.stringify(description)); + try { + let label: string = description?.label; + this.uiAbilityContext?.setMissionLabel(label).then(() => { + Log.d(PlatformPluginCallback.TAG, "Succeeded in seting mission label"); + }) + } catch (err) { + Log.d(PlatformPluginCallback.TAG, "Failed to set mission label: " + JSON.stringify(err)); + } + } + + showSystemOverlays(overlays: SystemUiOverlay[]) { + this.setSystemChromeEnabledSystemUIOverlays(overlays); + } + + showSystemUiMode(mode: SystemUiMode) { + this.setSystemChromeEnabledSystemUIMode(mode); + } + + setSystemUiChangeListener() { + this.platform?.setSystemChromeChangeListener(); + } + + restoreSystemUiOverlays() { + this.platform?.updateSystemUiOverlays(); + } + + setSystemUiOverlayStyle(systemUiOverlayStyle: SystemChromeStyle) { + Log.d(PlatformPluginCallback.TAG, "systemUiOverlayStyle:" + JSON.stringify(systemUiOverlayStyle)); + this.setSystemChromeSystemUIOverlayStyle(systemUiOverlayStyle); + } + + popSystemNavigator() { + if (this.platformPluginDelegate != null && this.platformPluginDelegate?.popSystemNavigator()) { + return; + } + router.back(); + } + + getClipboardData(result: MethodResult): void { + let atManager = abilityAccessCtrl.createAtManager(); + atManager.requestPermissionsFromUser(this.uiAbilityContext, ['ohos.permission.READ_PASTEBOARD']).then((data) => { + // https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-permissionrequestresult-V5 + // 相应请求权限的结果: + // -1:未授权,表示权限已设置,无需弹窗,需要用户在"设置"中修改 + // 0:已授权 + // 2:未授权,表示请求无效,可能原因有: + // -未在设置文件中声明目标权限。 + // -权限名非法。 + // -部分权限存在特殊申请条件,在申请对应权限时未满足其指定的条件 + enum AuthResultStatus { + NOT_CONFIGURED = -1, + GRANTED = 0, + INVALID_REQ = 2 + } + + let message: string = 'Failed to request permissions from user.'; + let authResult: number = data.authResults[0]; + switch (authResult) { + case AuthResultStatus.GRANTED: { + let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard(); + systemPasteboard.getData().then(async (pasteData: pasteboard.PasteData) => { + let pasteText: string = ''; + const recordCount: number = pasteData.getRecordCount(); + for (let i = 0; i < recordCount; i++) { + const record = pasteData.getRecord(i); + if (record.mimeType != pasteboard.MIMETYPE_TEXT_PLAIN + && record.mimeType != pasteboard.MIMETYPE_TEXT_HTML) { + continue; + } + let text: string = ''; + if (record.mimeType == pasteboard.MIMETYPE_TEXT_PLAIN){ + text = record.plainText; + } else if (record.mimeType == pasteboard.MIMETYPE_TEXT_HTML) { + const htmlText: StyledString = await StyledString.fromHtml(record.htmlText); + text = htmlText.getString(); + } + pasteText += text; + } + let response: Any = new Map().set("text", pasteText); + result.success(response); + }).catch((err: BusinessError) => { + Log.e(PlatformPluginCallback.TAG, "Failed to get PasteData. Cause: " + JSON.stringify(err)); + result.error("error", JSON.stringify(err), null); + }); + break; + } + case AuthResultStatus.NOT_CONFIGURED: { + message += 'Cause: Not configured in Settings'; + Log.i(PlatformPluginCallback.TAG, message); + result.success(null); + break; + } + case AuthResultStatus.INVALID_REQ: { + message += 'Cause: Invalid request'; + Log.i(PlatformPluginCallback.TAG, message); + result.success(null); + break; + } + default: { + message += `Unknown error: authResult=${authResult}`; + result.error("error", message, null); + break; + } + } + }).catch((err: BusinessError) => { + Log.e(PlatformPluginCallback.TAG, "Failed to request permissions from user. Cause: " + JSON.stringify(err)); + result.error("error", JSON.stringify(err), null); + }) + } + + setClipboardData(text: string, result: MethodResult) { + let pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text); + let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard(); + try { + systemPasteboard.setDataSync(pasteData); + result.success(null); + } catch (err) { + Log.d(PlatformPluginCallback.TAG, "Failed to set PasteData. Cause: " + JSON.stringify(err)); + result.error("error", JSON.stringify(err), null); + } + } + + clipboardHasStrings(): boolean { + return false; + } + + setSystemChromeEnabledSystemUIMode(mode: SystemUiMode): void { + Log.d(PlatformPluginCallback.TAG, "mode: " + mode); + let uiConfig: ('status' | 'navigation')[] = []; + if (mode == SystemUiMode.LEAN_BACK) { + //全屏显示,通过点击显示器上的任何位置都可以显示状态和导航栏 + FlutterManager.getInstance().setUseFullScreen(true, null); + } else if (mode == SystemUiMode.IMMERSIVE) { + //全屏显示,通过在显示器边缘的滑动手势可以显示状态和导航栏,应用程序不会接收到此手势 + FlutterManager.getInstance().setUseFullScreen(true, null); + } else if (mode == SystemUiMode.IMMERSIVE_STICKY) { + //全屏显示,通过在显示器边缘的滑动手势可以显示状态和导航栏,此手势由应用程序接收 + FlutterManager.getInstance().setUseFullScreen(true, null); + } else if (mode == SystemUiMode.EDGE_TO_EDGE) { + uiConfig = ['status', 'navigation']; + } else { + return; + } + this.showBarOrNavigation = uiConfig; + this.platform?.updateSystemUiOverlays(); + } + + setSystemChromeSystemUIOverlayStyle(systemChromeStyle: SystemChromeStyle): void { + let isStatusBarLightIconValue: boolean = false; + let statusBarContentColorValue: string | undefined = undefined; + let statusBarColorValue: string | undefined = undefined; + let navigationBarColorValue: string | undefined = undefined; + let isNavigationBarLightIconValue: boolean = false; + + const currentProps = this.mainWindow?.getWindowSystemBarProperties(); + + if (systemChromeStyle.statusBarIconBrightness != null) { + switch (systemChromeStyle.statusBarIconBrightness) { + case Brightness.DARK: + isStatusBarLightIconValue = false; + statusBarContentColorValue = '#000000'; + break; + case Brightness.LIGHT: + isStatusBarLightIconValue = true; + statusBarContentColorValue = '#FFFFFF'; + break; + } + } else { + isStatusBarLightIconValue = currentProps?.isStatusBarLightIcon ?? false + } + + if (systemChromeStyle.statusBarColor != null) { + statusBarColorValue = "#" + systemChromeStyle.statusBarColor.toString(16).padStart(8, '0'); + } else { + statusBarColorValue = currentProps?.statusBarColor + } + + if (systemChromeStyle.systemStatusBarContrastEnforced != null) { + + } + + if (systemChromeStyle.systemNavigationBarIconBrightness != null) { + switch (systemChromeStyle.systemNavigationBarIconBrightness) { + case Brightness.DARK: + isNavigationBarLightIconValue = true; + break; + case Brightness.LIGHT: + isNavigationBarLightIconValue = false; + } + } else { + isNavigationBarLightIconValue = currentProps?.isNavigationBarLightIcon ?? false + } + + if (systemChromeStyle.systemNavigationBarColor != null) { + navigationBarColorValue = "#" + systemChromeStyle.systemNavigationBarColor.toString(16).padStart(8, '0'); + } else { + navigationBarColorValue = currentProps?.navigationBarColor + } + + if (systemChromeStyle.systemNavigationBarContrastEnforced != null) { + + } + this.currentTheme = systemChromeStyle; + const systemBarProperties: window.SystemBarProperties = { + statusBarColor: statusBarColorValue, + isStatusBarLightIcon: isStatusBarLightIconValue, + statusBarContentColor: statusBarContentColorValue, + navigationBarColor: navigationBarColorValue, + isNavigationBarLightIcon: isNavigationBarLightIconValue, + navigationBarContentColor: currentProps?.navigationBarContentColor, + enableStatusBarAnimation: currentProps?.enableStatusBarAnimation, + enableNavigationBarAnimation: currentProps?.enableNavigationBarAnimation, + } + Log.d(PlatformPluginCallback.TAG, "systemBarProperties: " + JSON.stringify(systemBarProperties)); + this.mainWindow?.setWindowSystemBarProperties(systemBarProperties); + } + + setSystemChromeEnabledSystemUIOverlays(overlays: SystemUiOverlay[]): void { + let uiConfig: ('status' | 'navigation')[] = []; + if (overlays.length == 0) { + + } + for (let index = 0; index < overlays.length; ++index) { + let overlayToShow = overlays[index]; + switch (overlayToShow) { + case SystemUiOverlay.TOP_OVERLAYS: + uiConfig.push('status'); //hide navigation + break; + case SystemUiOverlay.BOTTOM_OVERLAYS: + uiConfig.push('navigation'); //hide bar + break; + } + } + this.showBarOrNavigation = uiConfig; + this.platform?.updateSystemUiOverlays(); + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/Any.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/Any.ets new file mode 100644 index 0000000000000000000000000000000000000000..3686519ffd796d35ecec8be176f2075683104be2 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/Any.ets @@ -0,0 +1,18 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +declare type Any = ESObject; + +export default Any; \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BackgroundBasicMessageChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BackgroundBasicMessageChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..e445bf994db908ee17f32d4e26d8373b70928fec --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BackgroundBasicMessageChannel.ets @@ -0,0 +1,137 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import MessageChannelUtils from '../../util/MessageChannelUtils'; +import Log from '../../util/Log'; +import { BinaryReply } from './BinaryMessenger'; +import { TaskQueue } from './BinaryMessenger'; +import MessageCodec from './MessageCodec'; +import { BinaryMessenger } from './BinaryMessenger'; +import SendableBinaryMessageHandler from './SendableBinaryMessageHandler' +import SendableMessageCodec from './SendableMessageCodec'; +import SendableMessageHandler from './SendableMessageHandler'; +import StringUtils from '../../util/StringUtils'; + +/** + * A named channel for communicating with the Flutter application using basic, asynchronous message + * passing. + * + *

Messages are encoded into binary before being sent, and binary messages received are decoded + * into Java objects. The {@link MessageCodec} used must be compatible with the one used by the + * Flutter application. This can be achieved by creating a BasicMessageChannel + * counterpart of this channel on the Dart side. The static Java type of messages sent and received + * is {@code Object}, but only values supported by the specified {@link MessageCodec} can be used. + * + *

The logical identity of the channel is given by its name. Identically named channels will + * interfere with each other's communication. + */ +export default class BackgroundBasicMessageChannel { + public static TAG = "BackgroundBasicMessageChannel#"; + public static CHANNEL_BUFFERS_CHANNEL = "dev.flutter/channel-buffers"; + private messenger: BinaryMessenger; + private name: string; + private codec: SendableMessageCodec; + private taskQueue: TaskQueue; + + constructor(messenger: BinaryMessenger, name: string, codec: SendableMessageCodec, taskQueue?: TaskQueue) { + this.messenger = messenger + this.name = name + this.codec = codec + this.taskQueue = taskQueue ?? messenger.makeBackgroundTaskQueue() + } + + /** + * Sends the specified message to the Flutter application, optionally expecting a reply. + * + *

Any uncaught exception thrown by the reply callback will be caught and logged. + * + * @param message the message, possibly null. + * @param callback a {@link Reply} callback, possibly null. + */ + send(message: T, callback?: (reply: T) => void): void { + this.messenger.send(this.name, this.codec.encodeMessage(message), + callback == null ? null : new IncomingReplyHandler(callback, this.codec)); + } + + /** + * Registers a message handler on this channel for receiving messages sent from the Flutter + * application. + * + *

Overrides any existing handler registration for (the name of) this channel. + * + *

If no handler has been registered, any incoming message on this channel will be handled + * silently by sending a null reply. + * + * @param handler a {@link MessageHandler}, or null to deregister. + */ + setMessageHandler(handler: SendableMessageHandler | null): void { + this.messenger.setMessageHandler(this.name, + handler == null ? null : new IncomingSendableMessageHandler(handler, this.codec), this.taskQueue); + } + + /** + * Adjusts the number of messages that will get buffered when sending messages to channels that + * aren't fully set up yet. For example, the engine isn't running yet or the channel's message + * handler isn't set up on the Dart side yet. + */ + resizeChannelBuffer(newSize: number): void { + MessageChannelUtils.resizeChannelBuffer(this.messenger, this.name, newSize); + } +} + + +class IncomingReplyHandler implements BinaryReply { + private callback: (reply: T) => void; + private codec: SendableMessageCodec + + constructor(callback: (reply: T) => void, codec: SendableMessageCodec) { + this.callback = callback + this.codec = codec + } + + reply(reply: ArrayBuffer | null) { + try { + this.callback(this.codec.decodeMessage(reply)); + } catch (e) { + Log.e(BackgroundBasicMessageChannel.TAG, "Failed to handle message reply", e); + } + } +} + +@Sendable +class IncomingSendableMessageHandler implements SendableBinaryMessageHandler { + private handler: SendableMessageHandler + private codec: SendableMessageCodec + + constructor(handler: SendableMessageHandler, codec: SendableMessageCodec) { + this.handler = handler; + this.codec = codec + } + + onMessage(message: ArrayBuffer, callback: BinaryReply) { + try { + this.handler.onMessage( + this.codec.decodeMessage(message), + { + reply: (reply: T): void => { + callback.reply(this.codec.encodeMessage(reply)); + } + }); + } catch (e) { + Log.e('WARNNING', "Failed to handle message: ", e); + callback.reply(StringUtils.stringToArrayBuffer("")); + } + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BackgroundMethodChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BackgroundMethodChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..5aad41f579c3228fc258f8cc54331fce45031a80 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BackgroundMethodChannel.ets @@ -0,0 +1,162 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import Log from '../../util/Log'; +import MessageChannelUtils from '../../util/MessageChannelUtils'; +import StringUtils from '../../util/StringUtils'; +import { BinaryMessenger, BinaryReply, TaskQueue } from './BinaryMessenger'; +import Any from './Any'; +import MethodCall from './MethodCall'; +import MethodCodec from './MethodCodec'; +import { MethodResult } from './MethodChannel' +import SendableStandardMethodCodec from './SendableStandardMethodCodec'; +import SendableMethodCallHandler from './SendableMethodCallHandler' +import SendableMethodCodec from './SendableMethodCodec' +import SendableBinaryMessageHandler from './SendableBinaryMessageHandler' + +/** + * A named channel for communicating with the Flutter application using asynchronous method calls. + * + *

Incoming method calls are decoded from binary on receipt, and Java results are encoded into + * binary before being transmitted back to Flutter. The {@link MethodCodec} used must be compatible + * with the one used by the Flutter application. This can be achieved by creating a MethodChannel + * counterpart of this channel on the Dart side. The Java type of method call arguments and results + * is {@code Object}, but only values supported by the specified {@link MethodCodec} can be used. + * + *

The logical identity of the channel is given by its name. Identically named channels will + * interfere with each other's communication. + */ + +export default class BackgroundMethodChannel { + static TAG = "BackgroundMethodChannel#"; + private messenger: BinaryMessenger; + private name: string; + private codec: SendableMethodCodec; + private taskQueue: TaskQueue; + private args: Object[]; + + constructor(messenger: BinaryMessenger, + name: string, + codec: SendableMethodCodec = SendableStandardMethodCodec.INSTANCE, + taskQueue?: TaskQueue, + ...args: Object[]) { + this.messenger = messenger + this.name = name + this.codec = codec + this.taskQueue = taskQueue ?? messenger.makeBackgroundTaskQueue() + this.args = args + } + + /** + * Invokes a method on this channel, optionally expecting a result. + * + *

Any uncaught exception thrown by the result callback will be caught and logged. + * + * @param method the name String of the method. + * @param arguments the arguments for the invocation, possibly null. + * @param callback a {@link Result} callback for the invocation result, or null. + */ + invokeMethod(method: string, args: Any, callback?: MethodResult): void { + this.messenger.send(this.name, + this.codec.encodeMethodCall(new MethodCall(method, args)), + callback == null ? null : new IncomingSendableResultHandler(callback, this.codec)); + } + + /** + * Registers a method call handler on this channel. + * + *

Overrides any existing handler registration for (the name of) this channel. + * + *

If no handler has been registered, any incoming method call on this channel will be handled + * silently by sending a null reply. This results in a MissingPluginException + * on the Dart side, unless an OptionalMethodChannel + * is used. + * + * @param handler a {@link MethodCallHandler}, or null to deregister. + */ + setMethodCallHandler(handler: SendableMethodCallHandler | null): void { + this.messenger.setMessageHandler(this.name, + handler == null ? null : new IncomingSendableMethodCallHandler(handler, this.codec), + this.taskQueue, ...this.args); + } + + /** + * Adjusts the number of messages that will get buffered when sending messages to channels that + * aren't fully set up yet. For example, the engine isn't running yet or the channel's message + * handler isn't set up on the Dart side yet. + */ + resizeChannelBuffer(newSize: number): void { + MessageChannelUtils.resizeChannelBuffer(this.messenger, this.name, newSize); + } +} + +export class IncomingSendableResultHandler implements BinaryReply { + private callback: MethodResult; + private codec: SendableMethodCodec; + + constructor(callback: MethodResult, codec: SendableMethodCodec) { + this.callback = callback; + this.codec = codec + } + + reply(reply: ArrayBuffer | null): void { + try { + if (reply == null) { + this.callback.notImplemented(); + } else { + try { + this.callback.success(this.codec.decodeEnvelope(reply)); + } catch (e) { + this.callback.error(e.code, e.getMessage(), e.details); + } + } + } catch (e) { + Log.e(BackgroundMethodChannel.TAG, "Failed to handle method call result", e); + } + } +} + +@Sendable +export class IncomingSendableMethodCallHandler implements SendableBinaryMessageHandler { + private handler: SendableMethodCallHandler; + private codec: SendableMethodCodec; + + constructor(handler: SendableMethodCallHandler, codec: SendableMethodCodec) { + this.handler = handler; + this.codec = codec; + } + + onMessage(message: ArrayBuffer, reply: BinaryReply, ...args: Object[]): void { + try { + this.handler.onMethodCall( + this.codec.decodeMethodCall(message), + { + success: (result: Any): void => { + reply.reply(this.codec.encodeSuccessEnvelope(result)); + }, + error: (errorCode: string, errorMessage: string, errorDetails: Any): void => { + reply.reply(this.codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails)); + }, + notImplemented: (): void => { + reply.reply(StringUtils.stringToArrayBuffer("")); + } + }, ...args); + } catch (e) { + reply.reply(this.codec.encodeErrorEnvelopeWithStacktrace("error", e.getMessage(), null, e)); + } + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BasicMessageChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BasicMessageChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..3bc61cc47e54115ec4b33d4e5cd107addc2ec8ff --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BasicMessageChannel.ets @@ -0,0 +1,163 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import MessageChannelUtils from '../../util/MessageChannelUtils'; +import { BinaryMessageHandler } from './BinaryMessenger'; +import Log from '../../util/Log'; +import { BinaryReply } from './BinaryMessenger'; +import { TaskQueue } from './BinaryMessenger'; +import MessageCodec from './MessageCodec'; +import { BinaryMessenger } from './BinaryMessenger'; +import StringUtils from '../../util/StringUtils'; + +/** + * A named channel for communicating with the Flutter application using basic, asynchronous message + * passing. + * + *

Messages are encoded into binary before being sent, and binary messages received are decoded + * into Java objects. The {@link MessageCodec} used must be compatible with the one used by the + * Flutter application. This can be achieved by creating a BasicMessageChannel + * counterpart of this channel on the Dart side. The static Java type of messages sent and received + * is {@code Object}, but only values supported by the specified {@link MessageCodec} can be used. + * + *

The logical identity of the channel is given by its name. Identically named channels will + * interfere with each other's communication. + */ +export default class BasicMessageChannel { + public static TAG = "BasicMessageChannel#"; + public static CHANNEL_BUFFERS_CHANNEL = "dev.flutter/channel-buffers"; + private messenger: BinaryMessenger; + private name: string; + private codec: MessageCodec; + + constructor(messenger: BinaryMessenger, name: string, codec: MessageCodec) { + this.messenger = messenger + this.name = name + this.codec = codec + } + + /** + * Sends the specified message to the Flutter application, optionally expecting a reply. + * + *

Any uncaught exception thrown by the reply callback will be caught and logged. + * + * @param message the message, possibly null. + * @param callback a {@link Reply} callback, possibly null. + */ + send(message: T, callback?: (reply: T) => void): void { + this.messenger.send(this.name, this.codec.encodeMessage(message), + callback == null ? null : new IncomingReplyHandler(callback, this.codec)); + } + + /** + * Registers a message handler on this channel for receiving messages sent from the Flutter + * application. + * + *

Overrides any existing handler registration for (the name of) this channel. + * + *

If no handler has been registered, any incoming message on this channel will be handled + * silently by sending a null reply. + * + * @param handler a {@link MessageHandler}, or null to deregister. + */ + setMessageHandler(handler: MessageHandler | null): void { + this.messenger.setMessageHandler(this.name, + handler == null ? null : new IncomingMessageHandler(handler, this.codec)); + } + + /** + * Adjusts the number of messages that will get buffered when sending messages to channels that + * aren't fully set up yet. For example, the engine isn't running yet or the channel's message + * handler isn't set up on the Dart side yet. + */ + resizeChannelBuffer(newSize: number): void { + MessageChannelUtils.resizeChannelBuffer(this.messenger, this.name, newSize); + } +} + + +export interface Reply { + /** + * Handles the specified message reply. + * + * @param reply the reply, possibly null. + */ + reply: (reply: T) => void; +} + +export interface MessageHandler { + + /** + * Handles the specified message received from Flutter. + * + *

Handler implementations must reply to all incoming messages, by submitting a single reply + * message to the given {@link Reply}. Failure to do so will result in lingering Flutter reply + * handlers. The reply may be submitted asynchronously and invoked on any thread. + * + *

Any uncaught exception thrown by this method, or the preceding message decoding, will be + * caught by the channel implementation and logged, and a null reply message will be sent back + * to Flutter. + * + *

Any uncaught exception thrown during encoding a reply message submitted to the {@link + * Reply} is treated similarly: the exception is logged, and a null reply is sent to Flutter. + * + * @param message the message, possibly null. + * @param reply a {@link Reply} for sending a single message reply back to Flutter. + */ + onMessage(message: T, reply: Reply): void; +} + +class IncomingReplyHandler implements BinaryReply { + private callback: (reply: T) => void; + private codec: MessageCodec + + constructor(callback: (reply: T) => void, codec: MessageCodec) { + this.callback = callback + this.codec = codec + } + + reply(reply: ArrayBuffer | null) { + try { + this.callback(this.codec.decodeMessage(reply)); + } catch (e) { + Log.e(BasicMessageChannel.TAG, "Failed to handle message reply", e); + } + } +} + +class IncomingMessageHandler implements BinaryMessageHandler { + private handler: MessageHandler + private codec: MessageCodec + + constructor(handler: MessageHandler, codec: MessageCodec) { + this.handler = handler; + this.codec = codec + } + + onMessage(message: ArrayBuffer, callback: BinaryReply) { + try { + this.handler.onMessage( + this.codec.decodeMessage(message), + { + reply: (reply: T): void => { + callback.reply(this.codec.encodeMessage(reply)); + } + }); + } catch (e) { + Log.e(BasicMessageChannel.TAG, "Failed to handle message", e); + callback.reply(StringUtils.stringToArrayBuffer("")); + } + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BinaryCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BinaryCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..1c15c762108efc389aef0b9d2bf3ff8b07b01991 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BinaryCodec.ets @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import MessageCodec from './MessageCodec'; + +/** + * A {@link MessageCodec} using unencoded binary messages, represented as {@link ByteBuffer}s. + * + *

This codec is guaranteed to be compatible with the corresponding BinaryCodec on the + * Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

On the Dart side, messages are represented using {@code ByteData}. + */ + +export default class BinaryCodec implements MessageCodec { + private returnsDirectByteBufferFromDecoding: boolean = false; + static readonly INSTANCE_DIRECT = new BinaryCodec(true); + + constructor(returnsDirectByteBufferFromDecoding: boolean) { + this.returnsDirectByteBufferFromDecoding = returnsDirectByteBufferFromDecoding; + } + + encodeMessage(message: ArrayBuffer): ArrayBuffer { + return message + } + + decodeMessage(message: ArrayBuffer | null): ArrayBuffer { + if (message == null) { + return new ArrayBuffer(0); + } else if (this.returnsDirectByteBufferFromDecoding) { + return message; + } else { + return message.slice(0, message.byteLength); + } + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BinaryMessenger.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BinaryMessenger.ets new file mode 100644 index 0000000000000000000000000000000000000000..6f98b2015638a53b1f2a9fb024c6336105af2e56 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BinaryMessenger.ets @@ -0,0 +1,169 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +/** + * An abstraction over the threading policy used to invoke message handlers. + * + *

These are generated by calling methods like {@link + * BinaryMessenger#makeBackgroundTaskQueue(TaskQueueOptions)} and can be passed into platform + * channels' constructors to control the threading policy for handling platform channels' + * messages. + */ + +import SendableBinaryMessageHandler from './SendableBinaryMessageHandler' + +export interface TaskQueue {} + +/** + * The priority of task execution + * + * This priority is guaranteed to be compatible with + * https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-taskpool-V5#priority. + * + */ +export enum TaskPriority { + HIGH = 0, + MEDIUM = 1, + LOW = 2, + IDLE = 3 +} + +/** Options that control how a TaskQueue should operate and be created. */ +export class TaskQueueOptions { + private isSerial: boolean = true; + private isSingleThread: boolean = false; + private priority: TaskPriority = TaskPriority.MEDIUM; + + getIsSerial():boolean { + return this.isSerial; + } + + setIsSerial(isSerial: boolean): TaskQueueOptions { + this.isSerial = isSerial; + return this; + } + + getPriority(): TaskPriority { + return this.priority; + } + + setPriority(priority: TaskPriority): TaskQueueOptions { + this.priority = priority; + return this; + } + + isSingleThreadMode(): boolean { + return this.isSingleThread; + } + + setSingleThreadMode(isSingleThread: boolean): TaskQueueOptions { + this.isSingleThread = isSingleThread; + return this; + } +} + +/** + * Binary message reply callback. Used to submit a reply to an incoming message from Flutter. Also + * used in the dual capacity to handle a reply received from Flutter after sending a message. + */ +export interface BinaryReply { + /** + * Handles the specified reply. + * + * @param reply the reply payload, a direct-allocated {@link ByteBuffer} or null. Senders of + * outgoing replies must place the reply bytes between position zero and current position. + * Reply receivers can read from the buffer directly. + */ + reply: (reply: ArrayBuffer | null) => void; +} + +/** Handler for incoming binary messages from Flutter. */ +export interface BinaryMessageHandler { + /** + * Handles the specified message. + * + *

Handler implementations must reply to all incoming messages, by submitting a single reply + * message to the given {@link BinaryReply}. Failure to do so will result in lingering Flutter + * reply handlers. The reply may be submitted asynchronously. + * + *

Any uncaught exception thrown by this method will be caught by the messenger + * implementation and logged, and a null reply message will be sent back to Flutter. + * + * @param message the message {@link ByteBuffer} payload, possibly null. + * @param reply A {@link BinaryReply} used for submitting a reply back to Flutter. + */ + onMessage(message: ArrayBuffer, reply: BinaryReply): void; +} + +/** + * Facility for communicating with Flutter using asynchronous message passing with binary messages. + * The Flutter Dart code should use BinaryMessages to + * participate. + * + *

{@code BinaryMessenger} is expected to be utilized from a single thread throughout the + * duration of its existence. If created on the main thread, then all invocations should take place + * on the main thread. If created on a background thread, then all invocations should take place on + * that background thread. + * + * @see BasicMessageChannel , which supports message passing with Strings and semi-structured + * messages. + * @see MethodChannel , which supports communication using asynchronous method invocation. + * @see EventChannel , which supports communication using event streams. + */ + +export interface BinaryMessenger { + makeBackgroundTaskQueue(options?: TaskQueueOptions): TaskQueue; + + /** + * Sends a binary message to the Flutter application. + * + * @param channel the name {@link String} of the logical channel used for the message. + * @param message the message payload, a direct-allocated {@link ByteBuffer} with the message + * bytes between position zero and current position, or null. + */ + send(channel: String, message: ArrayBuffer | null): void; + + /** + * Sends a binary message to the Flutter application, optionally expecting a reply. + * + *

Any uncaught exception thrown by the reply callback will be caught and logged. + * + * @param channel the name {@link String} of the logical channel used for the message. + * @param message the message payload, a direct-allocated {@link ByteBuffer} with the message + * bytes between position zero and current position, or null. + * @param callback a {@link BinaryReply} callback invoked when the Flutter application responds to + * the message, possibly null. + */ + send(channel: String, message: ArrayBuffer, callback?: BinaryReply | null): void; + + /** + * Registers a handler to be invoked when the Flutter application sends a message to its host + * platform. + * + *

Registration overwrites any previous registration for the same channel name. Use a null + * handler to deregister. + * + *

If no handler has been registered for a particular channel, any incoming message on that + * channel will be handled silently by sending a null reply. + * + * @param channel the name {@link String} of the channel. + * @param handler a {@link BinaryMessageHandler} to be invoked on incoming messages, or null. + * @param taskQueue a {@link BinaryMessenger.TaskQueue} that specifies what thread will execute + * the handler. Specifying null means execute on the platform thread. + */ + setMessageHandler(channel: String, handler: BinaryMessageHandler | SendableBinaryMessageHandler | null, + taskQueue?: TaskQueue, ...args: Object[]): void; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/EventChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/EventChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..db100fdd554e8e711c64549a4f6e793eb02e3b2e --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/EventChannel.ets @@ -0,0 +1,269 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + + +/** + * A named channel for communicating with the Flutter application using asynchronous event streams. + * + *

Incoming requests for event stream setup are decoded from binary on receipt, and Java + * responses and events are encoded into binary before being transmitted back to Flutter. The {@link + * MethodCodec} used must be compatible with the one used by the Flutter application. This can be + * achieved by creating an EventChannel + * counterpart of this channel on the Dart side. The Java type of stream configuration arguments, + * events, and error details is {@code Object}, but only values supported by the specified {@link + * MethodCodec} can be used. + * + *

The logical identity of the channel is given by its name. Identically named channels will + * interfere with each other's communication. + */ +import Log from '../../util/Log'; +import { BinaryMessageHandler, BinaryMessenger, BinaryReply, TaskQueue } from './BinaryMessenger'; +import Any from './Any'; +import MethodCodec from './MethodCodec'; +import StandardMethodCodec from './StandardMethodCodec'; + +const TAG = "EventChannel#"; + +export default class EventChannel { + private messenger: BinaryMessenger; + private name: string; + private codec: MethodCodec; + private taskQueue: TaskQueue | null; + + constructor(messenger: BinaryMessenger, name: string, codec?: MethodCodec, taskQueue?: TaskQueue) { + this.messenger = messenger + this.name = name + this.codec = codec ? codec : StandardMethodCodec.INSTANCE + // TODO:(0xZOne): 实现后台处理 + // this.taskQueue = taskQueue ?? null + this.taskQueue = null + } + + + /** + * Registers a stream handler on this channel. + * + *

Overrides any existing handler registration for (the name of) this channel. + * + *

If no handler has been registered, any incoming stream setup requests will be handled + * silently by providing an empty stream. + * + * @param handler a {@link StreamHandler}, or null to deregister. + */ + setStreamHandler(handler: StreamHandler): void { + // We call the 2 parameter variant specifically to avoid breaking changes in + // mock verify calls. + // See https://github.com/flutter/flutter/issues/92582. + if (this.taskQueue != null) { + this.messenger.setMessageHandler( + this.name, + handler == null ? null : new IncomingStreamRequestHandler(handler, this.name, this.codec, this.messenger), + this.taskQueue); + } else { + this.messenger.setMessageHandler( + this.name, + handler == null ? null : new IncomingStreamRequestHandler(handler, this.name, this.codec, this.messenger)); + } + } +} + +/** + * Handler of stream setup and teardown requests. + * + *

Implementations must be prepared to accept sequences of alternating calls to {@link + * #onListen(Object, EventChannel.EventSink)} and {@link #onCancel(Object)}. Implementations + * should ideally consume no resources when the last such call is not {@code onListen}. In typical + * situations, this means that the implementation should register itself with platform-specific + * event sources {@code onListen} and deregister again {@code onCancel}. + */ +export interface StreamHandler { + /** + * Handles a request to set up an event stream. + * + *

Any uncaught exception thrown by this method will be caught by the channel implementation + * and logged. An error result message will be sent back to Flutter. + * + * @param arguments stream configuration arguments, possibly null. + * @param events an {@link EventSink} for emitting events to the Flutter receiver. + */ + onListen(args: Any, events: EventSink): void; + + /** + * Handles a request to tear down the most recently created event stream. + * + *

Any uncaught exception thrown by this method will be caught by the channel implementation + * and logged. An error result message will be sent back to Flutter. + * + *

The channel implementation may call this method with null arguments to separate a pair of + * two consecutive set up requests. Such request pairs may occur during Flutter hot restart. Any + * uncaught exception thrown in this situation will be logged without notifying Flutter. + * + * @param arguments stream configuration arguments, possibly null. + */ + onCancel(args: Any): void; +} + +/** + * Event callback. Supports dual use: Producers of events to be sent to Flutter act as clients of + * this interface for sending events. Consumers of events sent from Flutter implement this + * interface for handling received events (the latter facility has not been implemented yet). + */ +export interface EventSink { + /** + * Consumes a successful event. + * + * @param event the event, possibly null. + */ + success(event: Any): void; + + /** + * Consumes an error event. + * + * @param errorCode an error code String. + * @param errorMessage a human-readable error message String, possibly null. + * @param errorDetails error details, possibly null + */ + error(errorCode: string, errorMessage: string, errorDetails: Any): void; + + /** + * Consumes end of stream. Ensuing calls to {@link #success(Object)} or {@link #error(String, + * String, Object)}, if any, are ignored. + */ + endOfStream(): void; +} + +class IncomingStreamRequestHandler implements BinaryMessageHandler { + private handler: StreamHandler; + private activeSink = new AtomicReference(null); + private codec: MethodCodec; + private name: string; + private messenger: BinaryMessenger; + + constructor(handler: StreamHandler, name: string, codec: MethodCodec, messenger: BinaryMessenger) { + this.handler = handler; + this.codec = codec; + this.name = name; + this.messenger = messenger; + } + + onMessage(message: ArrayBuffer, reply: BinaryReply): void { + const call = this.codec.decodeMethodCall(message); + if (call.method == "listen") { + this.onListen(call.args, reply); + } else if (call.method == "cancel") { + this.onCancel(call.args, reply); + } else { + reply.reply(null); + } + } + + onListen(args: Any, callback: BinaryReply): void { + const eventSink = new EventSinkImplementation(this.activeSink, this.name, this.codec, this.messenger); + const oldSink = this.activeSink.getAndSet(eventSink); + if (oldSink != null) { + // Repeated calls to onListen may happen during hot restart. + // We separate them with a call to onCancel. + try { + this.handler.onCancel(null); + } catch (e) { + Log.e(TAG + this.name, "Failed to close existing event stream", e); + } + } + try { + this.handler.onListen(args, eventSink); + callback.reply(this.codec.encodeSuccessEnvelope(null)); + } catch (e) { + this.activeSink.set(null); + Log.e(TAG + this.name, "Failed to open event stream", e); + callback.reply(this.codec.encodeErrorEnvelope("error", e.getMessage(), null)); + } + } + + onCancel(args: Any, callback: BinaryReply): void { + const oldSink = this.activeSink.getAndSet(null); + if (oldSink != null) { + try { + this.handler.onCancel(args); + callback.reply(this.codec.encodeSuccessEnvelope(null)); + } catch (e) { + Log.e(TAG + this.name, "Failed to close event stream", e); + callback.reply(this.codec.encodeErrorEnvelope("error", e.getMessage(), null)); + } + } else { + callback.reply(this.codec.encodeErrorEnvelope("error", "No active stream to cancel", null)); + } + } +} + +class EventSinkImplementation implements EventSink { + private hasEnded = false; + private activeSink: AtomicReference; + private messenger: BinaryMessenger; + private codec: MethodCodec; + private name: string; + + constructor(activeSink: AtomicReference, name: string, codec: MethodCodec, messenger: BinaryMessenger) { + this.activeSink = activeSink; + this.codec = codec; + this.name = name; + this.messenger = messenger; + } + + success(event: Any): void { + if (this.hasEnded || this.activeSink.get() != this) { + return; + } + this.messenger.send(this.name, this.codec.encodeSuccessEnvelope(event)); + } + + error(errorCode: string, errorMessage: string, errorDetails: Any) { + if (this.hasEnded || this.activeSink.get() != this) { + return; + } + this.messenger.send( + this.name, this.codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails)); + } + + endOfStream(): void { + if (this.hasEnded || this.activeSink.get() != this) { + return; + } + this.hasEnded = true; + this.messenger.send(this.name, new ArrayBuffer(0)); + } +} + +class AtomicReference { + private value: T | null; + + constructor(value: T | null) { + this.value = value + } + + get(): T | null { + return this.value; + } + + set(newValue: T | null): void { + this.value = newValue; + } + + getAndSet(newValue: T | null) { + const oldValue = this.value; + this.value = newValue; + return oldValue; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/FlutterException.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/FlutterException.ets new file mode 100644 index 0000000000000000000000000000000000000000..f52be9108c6f35b9bae48d114643cef71f6ed592 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/FlutterException.ets @@ -0,0 +1,29 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import Any from './Any'; + +export default class FlutterException implements Error { + stack?: string; + message: string; + name: string = ""; + code: string; + details: Any + + constructor(code: string, message: string, details: Any) { + this.message = message; + this.code = code; + this.details = details; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/JSONMessageCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/JSONMessageCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..509f2f45962b938dfcac46b34a390eafc503fe44 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/JSONMessageCodec.ets @@ -0,0 +1,101 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import StringUtils from '../../util/StringUtils'; + +import MessageCodec from './MessageCodec'; +import StringCodec from './StringCodec'; +import TreeMap from '@ohos.util.TreeMap'; +import HashMap from '@ohos.util.HashMap'; +import LightWeightMap from '@ohos.util.LightWeightMap'; +import PlainArray from '@ohos.util.PlainArray'; +import List from '@ohos.util.List'; +import LinkedList from '@ohos.util.LinkedList'; +import Any from './Any'; + +/** + * A {@link MessageCodec} using UTF-8 encoded JSON method calls and result envelopes. + * + *

This codec is guaranteed to be compatible with the corresponding JSONMethodCodec on + * the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

On the Dart side, JSON messages are handled by the JSON facilities of the dart:convert package. + */ +export default class JSONMessageCodec implements MessageCodec { + static INSTANCE = new JSONMessageCodec(); + + encodeMessage(message: Any): ArrayBuffer { + if (message == null) { + return StringUtils.stringToArrayBuffer(""); + } + return StringCodec.INSTANCE.encodeMessage(JSON.stringify(this.toBaseData(message))); + } + + decodeMessage(message: ArrayBuffer | null): Any { + if (message == null) { + return StringUtils.stringToArrayBuffer(""); + } + try { + const jsonStr = StringCodec.INSTANCE.decodeMessage(message); + let jsonObj: Record = JSON.parse(jsonStr); + if (jsonObj instanceof Object) { + const list = Object.keys(jsonObj); + if (list.includes('args')) { + let args: Any = jsonObj['args']; + if (args instanceof Object && !(args instanceof Array)) { + let argsMap: Map = new Map(); + Object.keys(args).forEach(key => { + argsMap.set(key, args[key]); + }) + jsonObj['args'] = argsMap; + } + } + } + return jsonObj; + } catch (e) { + throw new Error("Invalid JSON"); + } + } + + toBaseData(message: Any): Any { + if (message == null || message == undefined) { + return null; + } else if (message instanceof List || message instanceof LinkedList) { + return this.toBaseData(message.convertToArray()); + } else if (message instanceof Map || message instanceof HashMap || message instanceof TreeMap + || message instanceof LightWeightMap || message instanceof PlainArray) { + let messageObj: Any = {}; + message.forEach((value: Any, key: Any) => { + messageObj[this.toBaseData(key)] = this.toBaseData(value); + }); + return messageObj; + } else if (message instanceof Array) { + let messageArr: Array = []; + message.forEach((value: Any) => { + messageArr.push(this.toBaseData(value)); + }) + return messageArr; + } else if (message instanceof Object) { + let messageObj: Any = {}; + Object.keys(message).forEach((key: Any) => { + messageObj[this.toBaseData(key)] = this.toBaseData(message[key]); + }) + return messageObj; + } else { + return message; + } + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/JSONMethodCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/JSONMethodCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..08bb894f1e52bcbff42175368092d6cb2c76698e --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/JSONMethodCodec.ets @@ -0,0 +1,99 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import Log from '../../util/Log'; + +import ToolUtils from '../../util/ToolUtils'; +import FlutterException from './FlutterException'; +import Any from './Any'; +import JSONMessageCodec from './JSONMessageCodec'; +import MethodCall from './MethodCall'; +import MethodCodec from './MethodCodec'; + +/** + * A {@link MethodCodec} using UTF-8 encoded JSON method calls and result envelopes. + * + *

This codec is guaranteed to be compatible with the corresponding JSONMethodCodec on + * the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

Values supported as methods arguments and result payloads are those supported by {@link + * JSONMessageCodec}. + */ +export default class JSONMethodCodec implements MethodCodec { + static INSTANCE = new JSONMethodCodec(); + + encodeMethodCall(methodCall: MethodCall): ArrayBuffer { + try { + const map: Record = { + "method": methodCall.method, "args": methodCall.args + } + + return JSONMessageCodec.INSTANCE.encodeMessage(map); + } catch (e) { + throw new Error("Invalid JSON"); + } + } + + decodeMethodCall(message: ArrayBuffer): MethodCall { + try { + const json: Any = JSONMessageCodec.INSTANCE.decodeMessage(message); + if (ToolUtils.isObj(json)) { + const method: string = json["method"]; + const args: Any = json["args"]; + if (typeof method == 'string') { + return new MethodCall(method, args); + } + } + throw new Error("Invalid method call: " + json); + } catch (e) { + throw new Error("Invalid JSON:" + JSON.stringify(e)); + } + } + + encodeSuccessEnvelope(result: Any): ArrayBuffer { + return JSONMessageCodec.INSTANCE.encodeMessage([result]); + } + + encodeErrorEnvelope(errorCode: Any, errorMessage: string, errorDetails: Any): ArrayBuffer { + return JSONMessageCodec.INSTANCE.encodeMessage([errorCode, errorMessage, errorDetails]); + } + + encodeErrorEnvelopeWithStacktrace(errorCode: string, errorMessage: string, errorDetails: Any, + errorStacktrace: string): ArrayBuffer { + return JSONMessageCodec.INSTANCE.encodeMessage([errorCode, errorMessage, errorDetails, errorStacktrace]) + } + + decodeEnvelope(envelope: ArrayBuffer): Any { + try { + const json: Any = JSONMessageCodec.INSTANCE.decodeMessage(envelope); + if (json instanceof Array) { + if (json.length == 1) { + return json[0]; + } + if (json.length == 3) { + const code: string = json[0]; + const message: string = json[1]; + const details: Any = json[2]; + if (typeof code == 'string' && (message == null || typeof message == 'string')) { + throw new FlutterException(code, message, details); + } + } + } + throw new Error("Invalid envelope: " + json); + } catch (e) { + throw new Error("Invalid JSON"); + } + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MessageCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MessageCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..ccdfde77b362cebf704f5120c5e62277f4cf88e0 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MessageCodec.ets @@ -0,0 +1,30 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +/** + * A message encoding/decoding mechanism. + */ +export default interface MessageCodec { + /** + * Encodes the specified message into binary. + */ + encodeMessage(message: T): ArrayBuffer; + + /** + * Decodes the specified message from binary. + * + */ + decodeMessage(message: ArrayBuffer | null): T; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MethodCall.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MethodCall.ets new file mode 100644 index 0000000000000000000000000000000000000000..44c9029822acbbb62bfecb22bedb94a13078ba68 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MethodCall.ets @@ -0,0 +1,63 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import ToolUtils from '../../util/ToolUtils'; +import TreeMap from '@ohos.util.TreeMap'; +import HashMap from '@ohos.util.HashMap'; +import LightWeightMap from '@ohos.util.LightWeightMap'; +import Any from './Any'; + +/** Command object representing a method call on a {@link MethodChannel}. */ +export default class MethodCall { + /** The name of the called method. */ + method: string; + /** + * Arguments for the call. + * + *

Consider using {@link #arguments()} for cases where a particular run-time type is expected. + * Consider using {@link #argument(String)} when that run-time type is {@link Map} or {@link + * JSONObject}. + */ + args: Any; + + constructor(method: string, args: Any) { + this.method = method; + this.args = args; + } + + argument(key: string): Any { + if (this.args == null) { + return null; + } else if (this.args instanceof Map) { + return (this.args as Map).get(key); + } else if (ToolUtils.isObj(this.args)) { + return this.args[key]; + } else { + throw new Error("ClassCastException"); + } + } + + hasArgument(key: string): boolean { + if (this.args == null) { + return false; + } else if (this.args instanceof Map) { + return (this.args as Map).has(key); + } else if (ToolUtils.isObj(this.args)) { + return this.args.hasOwnProperty(key); + } else { + throw new Error("ClassCastException"); + } + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MethodChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MethodChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..22c36ac65e90dc425bf511051629f0e5f5a0f08a --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MethodChannel.ets @@ -0,0 +1,210 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import Log from '../../util/Log'; +import MessageChannelUtils from '../../util/MessageChannelUtils'; +import StringUtils from '../../util/StringUtils'; +import { BinaryMessageHandler, BinaryMessenger, BinaryReply } from './BinaryMessenger'; +import Any from './Any'; +import MethodCall from './MethodCall'; +import MethodCodec from './MethodCodec'; +import StandardMethodCodec from './StandardMethodCodec'; + +/** + * A named channel for communicating with the Flutter application using asynchronous method calls. + * + *

Incoming method calls are decoded from binary on receipt, and Java results are encoded into + * binary before being transmitted back to Flutter. The {@link MethodCodec} used must be compatible + * with the one used by the Flutter application. This can be achieved by creating a MethodChannel + * counterpart of this channel on the Dart side. The Java type of method call arguments and results + * is {@code Object}, but only values supported by the specified {@link MethodCodec} can be used. + * + *

The logical identity of the channel is given by its name. Identically named channels will + * interfere with each other's communication. + */ + +export default class MethodChannel { + static TAG = "MethodChannel#"; + private messenger: BinaryMessenger; + private name: string; + private codec: MethodCodec; + + constructor(messenger: BinaryMessenger, name: string, codec: MethodCodec = StandardMethodCodec.INSTANCE) { + this.messenger = messenger + this.name = name + this.codec = codec + } + + /** + * Invokes a method on this channel, optionally expecting a result. + * + *

Any uncaught exception thrown by the result callback will be caught and logged. + * + * @param method the name String of the method. + * @param arguments the arguments for the invocation, possibly null. + * @param callback a {@link Result} callback for the invocation result, or null. + */ + invokeMethod(method: string, args: Any, callback?: MethodResult): void { + this.messenger.send(this.name, this.codec.encodeMethodCall(new MethodCall(method, args)), + callback == null ? null : new IncomingResultHandler(callback, this.codec)); + } + + /** + * Registers a method call handler on this channel. + * + *

Overrides any existing handler registration for (the name of) this channel. + * + *

If no handler has been registered, any incoming method call on this channel will be handled + * silently by sending a null reply. This results in a MissingPluginException + * on the Dart side, unless an OptionalMethodChannel + * is used. + * + * @param handler a {@link MethodCallHandler}, or null to deregister. + */ + setMethodCallHandler(handler: MethodCallHandler | null): void { + this.messenger.setMessageHandler(this.name, + handler == null ? null : new IncomingMethodCallHandler(handler, this.codec)); + } + + /** + * Adjusts the number of messages that will get buffered when sending messages to channels that + * aren't fully set up yet. For example, the engine isn't running yet or the channel's message + * handler isn't set up on the Dart side yet. + */ + resizeChannelBuffer(newSize: number): void { + MessageChannelUtils.resizeChannelBuffer(this.messenger, this.name, newSize); + } +} + +/** A handler of incoming method calls. */ +export interface MethodCallHandler { + /** + * Handles the specified method call received from Flutter. + * + *

Handler implementations must submit a result for all incoming calls, by making a single + * call on the given {@link Result} callback. Failure to do so will result in lingering Flutter + * result handlers. The result may be submitted asynchronously and on any thread. Calls to + * unknown or unimplemented methods should be handled using {@link Result#notImplemented()}. + * + *

Any uncaught exception thrown by this method will be caught by the channel implementation + * and logged, and an error result will be sent back to Flutter. + * + *

The handler is called on the platform thread (HarmonyOS main thread) by default, or + * otherwise on the thread specified by the {@link BinaryMessenger.TaskQueue} provided to the + * associated {@link MethodChannel} when it was created. See also Threading in + * the Flutter Engine. + * + * @param call A {@link MethodCall}. + * @param result A {@link Result} used for submitting the result of the call. + */ + onMethodCall(call: MethodCall, result: MethodResult): void; +} + +/** + * Method call result callback. Supports dual use: Implementations of methods to be invoked by + * Flutter act as clients of this interface for sending results back to Flutter. Invokers of + * Flutter methods provide implementations of this interface for handling results received from + * Flutter. + * + *

All methods of this class can be invoked on any thread. + */ +export interface MethodResult { + /** + * Handles a successful result. + * + * @param result The result, possibly null. The result must be an Object type supported by the + * codec. For instance, if you are using {@link StandardMessageCodec} (default), please see + * its documentation on what types are supported. + */ + success: (result: Any) => void; + + /** + * Handles an error result. + * + * @param errorCode An error code String. + * @param errorMessage A human-readable error message String, possibly null. + * @param errorDetails Error details, possibly null. The details must be an Object type + * supported by the codec. For instance, if you are using {@link StandardMessageCodec} + * (default), please see its documentation on what types are supported. + */ + error: (errorCode: string, errorMessage: string, errorDetails: Any) => void; + + /** Handles a call to an unimplemented method. */ + notImplemented: () => void; +} + +export class IncomingResultHandler implements BinaryReply { + private callback: MethodResult; + private codec: MethodCodec; + + constructor(callback: MethodResult, codec: MethodCodec) { + this.callback = callback; + this.codec = codec + } + + reply(reply: ArrayBuffer | null): void { + try { + if (reply == null) { + this.callback.notImplemented(); + } else { + try { + this.callback.success(this.codec.decodeEnvelope(reply)); + } catch (e) { + this.callback.error(e.code, e.getMessage(), e.details); + } + } + } catch (e) { + Log.e(MethodChannel.TAG, "Failed to handle method call result", e); + } + } +} + +export class IncomingMethodCallHandler implements BinaryMessageHandler { + private handler: MethodCallHandler; + private codec: MethodCodec; + + constructor(handler: MethodCallHandler, codec: MethodCodec) { + this.handler = handler; + this.codec = codec + } + + onMessage(message: ArrayBuffer, reply: BinaryReply): void { + const call = this.codec.decodeMethodCall(message); + try { + this.handler.onMethodCall( + call, { + success: (result: Any): void => { + reply.reply(this.codec.encodeSuccessEnvelope(result)); + }, + + error: (errorCode: string, errorMessage: string, errorDetails: Any): void => { + reply.reply(this.codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails)); + }, + + notImplemented: (): void => { + Log.w(MethodChannel.TAG, "method not implemented"); + reply.reply(StringUtils.stringToArrayBuffer("")); + } + }); + } catch (e) { + Log.e(MethodChannel.TAG, "Failed to handle method call", e); + reply.reply(this.codec.encodeErrorEnvelopeWithStacktrace("error", e.getMessage(), null, e)); + } + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MethodCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MethodCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..31da8deff6f6d01164aa7bc34d6fa6bed6f5e504 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MethodCodec.ets @@ -0,0 +1,90 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import Any from './Any'; + +import MethodCall from './MethodCall'; + +/** + * A codec for method calls and enveloped results. + * + *

Method calls are encoded as binary messages with enough structure that the codec can extract a + * method name String and an arguments Object. These data items are used to populate a {@link + * MethodCall}. + * + *

All operations throw {@link IllegalArgumentException}, if conversion fails. + */ +export default interface MethodCodec { + /** + * Encodes a message call into binary. + * + * @param methodCall a {@link MethodCall}. + * @return a {@link ByteBuffer} containing the encoding between position 0 and the current + * position. + */ + encodeMethodCall(methodCall: MethodCall): ArrayBuffer; + + /** + * Decodes a message call from binary. + * + * @param methodCall the binary encoding of the method call as a {@link ByteBuffer}. + * @return a {@link MethodCall} representation of the bytes between the given buffer's current + * position and its limit. + */ + decodeMethodCall(methodCall: ArrayBuffer): MethodCall; + + /** + * Encodes a successful result into a binary envelope message. + * + * @param result The result value, possibly null. + * @return a {@link ByteBuffer} containing the encoding between position 0 and the current + * position. + */ + encodeSuccessEnvelope(result: Any): ArrayBuffer; + + /** + * Encodes an error result into a binary envelope message. + * + * @param errorCode An error code String. + * @param errorMessage An error message String, possibly null. + * @param errorDetails Error details, possibly null. Consider supporting {@link Throwable} in your + * codec. This is the most common value passed to this field. + * @return a {@link ByteBuffer} containing the encoding between position 0 and the current + * position. + */ + encodeErrorEnvelope(errorCode: string, errorMessage: string, errorDetails: Any): ArrayBuffer; + + /** + * Encodes an error result into a binary envelope message with the native stacktrace. + * + * @param errorCode An error code String. + * @param errorMessage An error message String, possibly null. + * @param errorDetails Error details, possibly null. Consider supporting {@link Throwable} in your + * codec. This is the most common value passed to this field. + * @param errorStacktrace Platform stacktrace for the error. possibly null. + * @return a {@link ByteBuffer} containing the encoding between position 0 and the current + * position. + */ + encodeErrorEnvelopeWithStacktrace(errorCode: string, errorMessage: string, errorDetails: Any, + errorStacktrace: string): ArrayBuffer + + /** + * Decodes a result envelope from binary. + * + * @param envelope the binary encoding of a result envelope as a {@link ByteBuffer}. + * @return the enveloped result Object. + * @throws FlutterException if the envelope was an error envelope. + */ + decodeEnvelope(envelope: ArrayBuffer): Any +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableBinaryCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableBinaryCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..3874e37028199fd8a68cf04b4a0ad25820491b97 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableBinaryCodec.ets @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import SendableMessageCodec from './SendableMessageCodec'; + +/** + * A {@link MessageCodec} using unencoded binary messages, represented as {@link ByteBuffer}s. + * + *

This codec is guaranteed to be compatible with the corresponding BinaryCodec on the + * Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

On the Dart side, messages are represented using {@code ByteData}. + */ + +@Sendable +export default class SendableBinaryCodec implements SendableMessageCodec { + private returnsDirectByteBufferFromDecoding: boolean = false; + static readonly INSTANCE_DIRECT: SendableBinaryCodec = new SendableBinaryCodec(true); + + constructor(returnsDirectByteBufferFromDecoding: boolean) { + this.returnsDirectByteBufferFromDecoding = returnsDirectByteBufferFromDecoding; + } + + encodeMessage(message: ArrayBuffer): ArrayBuffer { + return message + } + + decodeMessage(message: ArrayBuffer | null): ArrayBuffer { + if (message == null) { + return new ArrayBuffer(0); + } else if (this.returnsDirectByteBufferFromDecoding) { + return message; + } else { + return message.slice(0, message.byteLength); + } + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableBinaryMessageHandler.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableBinaryMessageHandler.ets new file mode 100644 index 0000000000000000000000000000000000000000..d4ac1dd2be1bee8a6469d37a1b6d613f721cf9fa --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableBinaryMessageHandler.ets @@ -0,0 +1,22 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import { lang } from '@kit.ArkTS'; +import { BinaryReply } from './BinaryMessenger'; + +type ISendable = lang.ISendable; + +export default interface SendableBinaryMessageHandler extends ISendable { + onMessage(message: ArrayBuffer, reply: BinaryReply, ...args: Object[]): void; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableJSONMessageCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableJSONMessageCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..3e3b2079cecb5aa14c2f100057a43476c5fd648e --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableJSONMessageCodec.ets @@ -0,0 +1,102 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import StringUtils from '../../util/StringUtils'; + +import SendableMessageCodec from './SendableMessageCodec'; +import StringCodec from './StringCodec'; +import TreeMap from '@ohos.util.TreeMap'; +import HashMap from '@ohos.util.HashMap'; +import LightWeightMap from '@ohos.util.LightWeightMap'; +import PlainArray from '@ohos.util.PlainArray'; +import List from '@ohos.util.List'; +import LinkedList from '@ohos.util.LinkedList'; +import Any from './Any'; + +/** + * A {@link MessageCodec} using UTF-8 encoded JSON method calls and result envelopes. + * + *

This codec is guaranteed to be compatible with the corresponding JSONMethodCodec on + * the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

On the Dart side, JSON messages are handled by the JSON facilities of the dart:convert package. + */ +@Sendable +export default class SendableJSONMessageCodec implements SendableMessageCodec { + static INSTANCE: SendableJSONMessageCodec = new SendableJSONMessageCodec(); + + encodeMessage(message: Any): ArrayBuffer { + if (message == null) { + return StringUtils.stringToArrayBuffer(""); + } + return StringCodec.INSTANCE.encodeMessage(JSON.stringify(this.toBaseData(message))); + } + + decodeMessage(message: ArrayBuffer | null): Any { + if (message == null) { + return StringUtils.stringToArrayBuffer(""); + } + try { + const jsonStr = StringCodec.INSTANCE.decodeMessage(message); + let jsonObj: Record = JSON.parse(jsonStr); + if (jsonObj instanceof Object) { + const list = Object.keys(jsonObj); + if (list.includes('args')) { + let args: Any = jsonObj['args']; + if (args instanceof Object && !(args instanceof Array)) { + let argsMap: Map = new Map(); + Object.keys(args).forEach(key => { + argsMap.set(key, args[key]); + }) + jsonObj['args'] = argsMap; + } + } + } + return jsonObj; + } catch (e) { + throw new Error("Invalid JSON"); + } + } + + toBaseData(message: Any): Any { + if (message == null || message == undefined) { + return ""; + } else if (message instanceof List || message instanceof LinkedList) { + return this.toBaseData(message.convertToArray()); + } else if (message instanceof Map || message instanceof HashMap || message instanceof TreeMap + || message instanceof LightWeightMap || message instanceof PlainArray) { + let messageObj: Any = {}; + message.forEach((value: Any, key: Any) => { + messageObj[this.toBaseData(key)] = this.toBaseData(value); + }); + return messageObj; + } else if (message instanceof Array) { + let messageArr: Array = []; + message.forEach((value: Any) => { + messageArr.push(this.toBaseData(value)); + }) + return messageArr; + } else if (message instanceof Object) { + let messageObj: Any = {}; + Object.keys(message).forEach((key: Any) => { + messageObj[this.toBaseData(key)] = this.toBaseData(message[key]); + }) + return messageObj; + } else { + return message; + } + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableJSONMethodCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableJSONMethodCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..5b3ba88814b83b8fbc8853a59bf337c8bd8f6bea --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableJSONMethodCodec.ets @@ -0,0 +1,98 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import ToolUtils from '../../util/ToolUtils'; +import FlutterException from './FlutterException'; +import Any from './Any'; +import SendableJSONMessageCodec from './SendableJSONMessageCodec'; +import MethodCall from './MethodCall'; +import SendableMethodCodec from './SendableMethodCodec'; + +/** + * A {@link SendableMethodCodec} using UTF-8 encoded JSON method calls and result envelopes. + * + *

This codec is guaranteed to be compatible with the corresponding JSONMethodCodec on + * the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

Values supported as methods arguments and result payloads are those supported by {@link + * SendableJSONMessageCodec}. + */ +@Sendable +export default class SendableJSONMethodCodec implements SendableMethodCodec { + static INSTANCE: SendableJSONMethodCodec = new SendableJSONMethodCodec(); + + encodeMethodCall(methodCall: MethodCall): ArrayBuffer { + try { + const map: Record = { + "method": methodCall.method, "args": methodCall.args + } + + return SendableJSONMessageCodec.INSTANCE.encodeMessage(map); + } catch (e) { + throw new Error("Invalid JSON"); + } + } + + decodeMethodCall(message: ArrayBuffer): MethodCall { + try { + const json: Any = SendableJSONMessageCodec.INSTANCE.decodeMessage(message); + if (ToolUtils.isObj(json)) { + const method: string = json["method"]; + const args: Any = json["args"]; + if (typeof method == 'string') { + return new MethodCall(method, args); + } + } + throw new Error("Invalid method call: " + json); + } catch (e) { + throw new Error("Invalid JSON:" + JSON.stringify(e)); + } + } + + encodeSuccessEnvelope(result: Any): ArrayBuffer { + return SendableJSONMessageCodec.INSTANCE.encodeMessage([result]); + } + + encodeErrorEnvelope(errorCode: Any, errorMessage: string, errorDetails: Any) { + return SendableJSONMessageCodec.INSTANCE.encodeMessage([errorCode, errorMessage, errorDetails]); + } + + encodeErrorEnvelopeWithStacktrace(errorCode: string, errorMessage: string, errorDetails: Any, + errorStacktrace: string): ArrayBuffer { + return SendableJSONMessageCodec.INSTANCE.encodeMessage([errorCode, errorMessage, errorDetails, errorStacktrace]) + } + + decodeEnvelope(envelope: ArrayBuffer): Any { + try { + const json: Any = SendableJSONMessageCodec.INSTANCE.decodeMessage(envelope); + if (json instanceof Array) { + if (json.length == 1) { + return json[0]; + } + if (json.length == 3) { + const code: string = json[0]; + const message: string = json[1]; + const details: Any = json[2]; + if (typeof code == 'string' && (message == null || typeof message == 'string')) { + throw new FlutterException(code, message, details); + } + } + } + throw new Error("Invalid envelope: " + json); + } catch (e) { + throw new Error("Invalid JSON"); + } + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMessageCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMessageCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..1afc0fc1a8ab6ba27db27ec7844a136adae6bd67 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMessageCodec.ets @@ -0,0 +1,30 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import { lang } from '@kit.ArkTS'; + +type ISendable = lang.ISendable; + +export default interface SendableMessageCodec extends ISendable { + /** + * Encodes the specified message into binary. + */ + encodeMessage(message: T): ArrayBuffer; + + /** + * Decodes the specified message from binary. + * + */ + decodeMessage(message: ArrayBuffer | null): T; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMessageHandler.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMessageHandler.ets new file mode 100644 index 0000000000000000000000000000000000000000..664bad580c8a7a0004e3ca90815f59aab2bb4a79 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMessageHandler.ets @@ -0,0 +1,22 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import { lang } from '@kit.ArkTS'; +import { Reply } from './BasicMessageChannel'; + +type ISendable = lang.ISendable; + +export default interface SendableMessageHandler extends ISendable { + onMessage(message: T, reply: Reply): void; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMethodCallHandler.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMethodCallHandler.ets new file mode 100644 index 0000000000000000000000000000000000000000..1df8fe0613f62f13eecc4158b0cd6cd48034410a --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMethodCallHandler.ets @@ -0,0 +1,45 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import { lang } from '@kit.ArkTS'; + +import MethodCall from './MethodCall'; +import { MethodResult } from './MethodChannel'; + +/** A handler of incoming method calls. */ +type ISendable = lang.ISendable; + +export default interface SendableMethodCallHandler extends ISendable { + /** + * Handles the specified method call received from Flutter. + * + *

Handler implementations must submit a result for all incoming calls, by making a single + * call on the given {@link Result} callback. Failure to do so will result in lingering Flutter + * result handlers. The result may be submitted asynchronously and on any thread. Calls to + * unknown or unimplemented methods should be handled using {@link Result#notImplemented()}. + * + *

Any uncaught exception thrown by this method will be caught by the channel implementation + * and logged, and an error result will be sent back to Flutter. + * + *

The handler is called on the platform thread (HarmonyOS main thread) by default, or + * otherwise on the thread specified by the {@link BinaryMessenger.TaskQueue} provided to the + * associated {@link MethodChannel} when it was created. See also Threading in + * the Flutter Engine. + * + * @param call A {@link MethodCall}. + * @param result A {@link Result} used for submitting the result of the call. + */ + onMethodCall(call: MethodCall, result: MethodResult, ...args: Object[]): void; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMethodCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMethodCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..01ff305b370ab3f8b93ac374cdf973175cfcc623 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMethodCodec.ets @@ -0,0 +1,93 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import { lang } from '@kit.ArkTS'; + +import Any from './Any'; +import MethodCall from './MethodCall'; + +/** + * A codec for method calls and enveloped results. + * + *

Method calls are encoded as binary messages with enough structure that the codec can extract a + * method name String and an arguments Object. These data items are used to populate a {@link + * MethodCall}. + * + *

All operations throw {@link IllegalArgumentException}, if conversion fails. + */ +type ISendable = lang.ISendable; + +export default interface SendableMethodCodec extends ISendable { + /** + * Encodes a message call into binary. + * + * @param methodCall a {@link MethodCall}. + * @return a {@link ByteBuffer} containing the encoding between position 0 and the current + * position. + */ + encodeMethodCall(methodCall: MethodCall): ArrayBuffer; + + /** + * Decodes a message call from binary. + * + * @param methodCall the binary encoding of the method call as a {@link ByteBuffer}. + * @return a {@link MethodCall} representation of the bytes between the given buffer's current + * position and its limit. + */ + decodeMethodCall(methodCall: ArrayBuffer): MethodCall; + + /** + * Encodes a successful result into a binary envelope message. + * + * @param result The result value, possibly null. + * @return a {@link ByteBuffer} containing the encoding between position 0 and the current + * position. + */ + encodeSuccessEnvelope(result: Any): ArrayBuffer; + + /** + * Encodes an error result into a binary envelope message. + * + * @param errorCode An error code String. + * @param errorMessage An error message String, possibly null. + * @param errorDetails Error details, possibly null. Consider supporting {@link Throwable} in your + * codec. This is the most common value passed to this field. + * @return a {@link ByteBuffer} containing the encoding between position 0 and the current + * position. + */ + encodeErrorEnvelope(errorCode: string, errorMessage: string, errorDetails: Any): ArrayBuffer; + + /** + * Encodes an error result into a binary envelope message with the native stacktrace. + * + * @param errorCode An error code String. + * @param errorMessage An error message String, possibly null. + * @param errorDetails Error details, possibly null. Consider supporting {@link Throwable} in your + * codec. This is the most common value passed to this field. + * @param errorStacktrace Platform stacktrace for the error. possibly null. + * @return a {@link ByteBuffer} containing the encoding between position 0 and the current + * position. + */ + encodeErrorEnvelopeWithStacktrace(errorCode: string, errorMessage: string, errorDetails: Any, + errorStacktrace: string): ArrayBuffer; + + /** + * Decodes a result envelope from binary. + * + * @param envelope the binary encoding of a result envelope as a {@link ByteBuffer}. + * @return the enveloped result Object. + * @throws FlutterException if the envelope was an error envelope. + */ + decodeEnvelope(envelope: ArrayBuffer): Any; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStandardMessageCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStandardMessageCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..dcc7d5a821d7489cf781bdbc74f1c52fd9bcb633 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStandardMessageCodec.ets @@ -0,0 +1,363 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import Any from './Any'; + +import { ByteBuffer } from '../../util/ByteBuffer'; +import SendableMessageCodec from './SendableMessageCodec'; +import StringUtils from '../../util/StringUtils'; +import TreeMap from '@ohos.util.TreeMap'; +import HashMap from '@ohos.util.HashMap'; +import LightWeightMap from '@ohos.util.LightWeightMap'; +import PlainArray from '@ohos.util.PlainArray'; +import List from '@ohos.util.List'; +import LinkedList from '@ohos.util.LinkedList'; + +/** + * MessageCodec using the Flutter standard binary encoding. + * + *

This codec is guaranteed to be compatible with the corresponding SendableStandardMessageCodec + * on the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

Supported messages are acyclic values of these forms: + * + *

    + *
  • null + *
  • Booleans + *
  • number + *
  • BigIntegers (see below) + *
  • Int8Array, Int32Array, Float32Array, Float64Array + *
  • Strings + *
  • Array[] + *
  • Lists of supported values + *
  • Maps with supported keys and values + *
+ * + *

On the Dart side, these values are represented as follows: + * + *

    + *
  • null: null + *
  • Boolean: bool + *
  • Byte, Short, Integer, Long: int + *
  • Float, Double: double + *
  • String: String + *
  • byte[]: Uint8List + *
  • int[]: Int32List + *
  • long[]: Int64List + *
  • float[]: Float32List + *
  • double[]: Float64List + *
  • List: List + *
  • Map: Map + *
+ * + *

BigIntegers are represented in Dart as strings with the hexadecimal representation of the + * integer's value. + * + *

To extend the codec, overwrite the writeValue and readValueOfType methods. + */ +@Sendable +export default class SendableStandardMessageCodec implements SendableMessageCodec { + static INSTANCE: SendableStandardMessageCodec = new SendableStandardMessageCodec(); + + encodeMessage(message: Any): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)) + this.writeValue(stream, message); + return stream.buffer + } + + decodeMessage(message: ArrayBuffer | null): Any { + if (message == null) { + return null + } + const buffer = ByteBuffer.from(message) + return this.readValue(buffer) + } + + private static NULL: number = 0; + private static TRUE: number = 1; + private static FALSE: number = 2; + private static INT32: number = 3; + private static INT64: number = 4; + private static BIGINT: number = 5; + private static FLOAT64: number = 6; + private static STRING: number = 7; + private static UINT8_ARRAY: number = 8; + private static INT32_ARRAY: number = 9; + private static INT64_ARRAY: number = 10; + private static FLOAT64_ARRAY: number = 11; + private static LIST: number = 12; + private static MAP: number = 13; + private static FLOAT32_ARRAY: number = 14; + + writeValue(stream: ByteBuffer, value: Any): Any { + if (value == null || value == undefined) { + stream.writeInt8(SendableStandardMessageCodec.NULL); + } else if (typeof value === "boolean") { + stream.writeInt8(value ? SendableStandardMessageCodec.TRUE : SendableStandardMessageCodec.FALSE) + } else if (typeof value === "number") { + if (Number.isInteger(value)) { //整型 + if (-0x7fffffff - 1 <= value && value <= 0x7fffffff) { //int32 + stream.writeInt8(SendableStandardMessageCodec.INT32); + stream.writeInt32(value, true); + } else if (Number.MIN_SAFE_INTEGER <= value && value <= Number.MAX_SAFE_INTEGER) { //int64 number整型取值范围 + stream.writeInt8(SendableStandardMessageCodec.INT64); + stream.writeInt64(value, true); + } else { //被判为整型的double型 + stream.writeInt8(SendableStandardMessageCodec.FLOAT64); + this.writeAlignment(stream, 8); + stream.writeFloat64(value, true); + } + } else { //浮点型 + stream.writeInt8(SendableStandardMessageCodec.FLOAT64); + this.writeAlignment(stream, 8); + stream.writeFloat64(value, true); + } + } else if (typeof value === "bigint") { + // https://api.flutter.dev/flutter/services/SendableStandardMessageCodec/writeValue.html + // + // The format is first the type byte (0x05), then the actual number + // as an ASCII string giving the hexadecimal representation of the + // integer, with the string's length as encoded by writeSize + // followed by the string bytes. + stream.writeInt8(SendableStandardMessageCodec.BIGINT); + // Convert bigint to a hexadecimal string + const hexString = value.toString(16); + // Map each character in the hexadecimal string to its ASCII code + const asciiString = hexString.split('').map(char => char.charCodeAt(0)); + this.writeBytes(stream, Uint8Array.from(asciiString)); + } else if (typeof value === "string") { + stream.writeInt8(SendableStandardMessageCodec.STRING); + let stringBuff = StringUtils.stringToArrayBuffer(value); + this.writeBytes(stream, new Uint8Array(stringBuff)); + } else if (value instanceof Uint8Array) { + stream.writeInt8(SendableStandardMessageCodec.UINT8_ARRAY); + this.writeBytes(stream, value) + } else if (value instanceof Int32Array) { + stream.writeInt8(SendableStandardMessageCodec.INT32_ARRAY); + this.writeSize(stream, value.length); + this.writeAlignment(stream, 4); + value.forEach(item => stream.writeInt32(item, true)); + } else if (value instanceof BigInt64Array) { + stream.writeInt8(SendableStandardMessageCodec.INT64_ARRAY); + this.writeSize(stream, value.length); + this.writeAlignment(stream, 8); + value.forEach(item => stream.writeBigInt64(item, true)); + } else if (value instanceof Float32Array) { + stream.writeInt8(SendableStandardMessageCodec.FLOAT32_ARRAY); + this.writeSize(stream, value.length); + this.writeAlignment(stream, 4); + value.forEach(item => stream.writeFloat32(item, true)); + } else if (value instanceof Float64Array) { + stream.writeInt8(SendableStandardMessageCodec.FLOAT64_ARRAY); + this.writeSize(stream, value.length); + this.writeAlignment(stream, 8); + value.forEach(item => stream.writeFloat64(item, true)); + } else if (value instanceof Array || value instanceof Int8Array || value instanceof Int16Array + || value instanceof Uint16Array || value instanceof Uint32Array || value instanceof List + || value instanceof LinkedList) { + stream.writeInt8(SendableStandardMessageCodec.LIST) + this.writeSize(stream, value.length); + value.forEach((item: Any): void => this.writeValue(stream, item)); + } else if (value instanceof Map) { + stream.writeInt8(SendableStandardMessageCodec.MAP); + this.writeSize(stream, value.size); + value.forEach((value: Any, key: Any) => { + this.writeValue(stream, key); + this.writeValue(stream, value); + }); + } else if (value instanceof HashMap || value instanceof TreeMap || value instanceof LightWeightMap + || value instanceof PlainArray) { + stream.writeInt8(SendableStandardMessageCodec.MAP); + this.writeSize(stream, value.length); + value.forEach((value: Any, key: Any) => { + this.writeValue(stream, key); + this.writeValue(stream, value); + }); + } else if (typeof value == 'object') { + let map: Map = new Map(); + Object.keys(value).forEach(key => { + map.set(key, value[key]); + }); + this.writeValue(stream, map); + } else { + throw new Error("Unsupported value: " + value); + stream.writeInt8(SendableStandardMessageCodec.NULL); + } + return stream; + } + + writeAlignment(stream: ByteBuffer, alignment: number) { + let mod: number = stream.byteOffset % alignment; + if (mod != 0) { + for (let i = 0; i < alignment - mod; i++) { + stream.writeInt8(0); + } + } + } + + writeSize(stream: ByteBuffer, value: number) { + if (value < 254) { + stream.writeUint8(value); + } else if (value <= 0xffff) { + stream.writeUint8(254); + stream.writeUint16(value, true); + } else { + stream.writeUint8(255); + stream.writeUint32(value, true); + } + } + + writeBytes(stream: ByteBuffer, bytes: Uint8Array) { + this.writeSize(stream, bytes.length) + stream.writeUint8Array(bytes); + } + + readSize(buffer: ByteBuffer) { + let value = buffer.readUint8() & 0xff; + if (value < 254) { + return value; + } else if (value == 254) { + return buffer.readUint16(true); + } else { + return buffer.readUint32(true); + } + } + + readAlignment(buffer: ByteBuffer, alignment: number) { + let mod = buffer.byteOffset % alignment; + if (mod != 0) { + buffer.skip(alignment - mod); + } + } + + readValue(buffer: ByteBuffer): Any { + let type = buffer.readUint8() + return this.readValueOfType(type, buffer); + } + + readBytes(buffer: ByteBuffer): Uint8Array { + let length = this.readSize(buffer); + let bytesBuffer = new ArrayBuffer(length); + let bytes = new Uint8Array(bytesBuffer); + bytes.set(buffer.readUint8Array(length)); + return bytes; + } + + readValueOfType(type: number, buffer: ByteBuffer): Any { + let result: Any; + switch (type) { + case SendableStandardMessageCodec.NULL: + result = null; + break; + case SendableStandardMessageCodec.TRUE: + result = true; + break; + case SendableStandardMessageCodec.FALSE: + result = false; + break; + case SendableStandardMessageCodec.INT32: + result = buffer.readInt32(true); + break; + case SendableStandardMessageCodec.INT64: + result = buffer.readInt64(true); + if (Number.MIN_SAFE_INTEGER <= result && result <= Number.MAX_SAFE_INTEGER) { + result = Number(result); + } + break; + case SendableStandardMessageCodec.BIGINT: + let bytes: Uint8Array = this.readBytes(buffer); + // Convert the byte array to a UTF-8 encoded string + const hexString: string = String.fromCharCode(...bytes); + // Parse the string as a hexadecimal BigInt + result = BigInt(`0x${hexString}`); + break; + case SendableStandardMessageCodec.FLOAT64: + this.readAlignment(buffer, 8); + result = buffer.readFloat64(true) + break; + case SendableStandardMessageCodec.STRING: { + let bytes: Uint8Array = this.readBytes(buffer); + result = StringUtils.uint8ArrayToString(bytes); + break; + } + case SendableStandardMessageCodec.UINT8_ARRAY: { + result = this.readBytes(buffer); + break; + } + case SendableStandardMessageCodec.INT32_ARRAY: { + let length = this.readSize(buffer); + let array = new Int32Array(length) + this.readAlignment(buffer, 4); + for (let i = 0; i < length; i++) { + array[i] = buffer.readInt32(true) + } + result = array; + break; + } + case SendableStandardMessageCodec.INT64_ARRAY: { + let length = this.readSize(buffer); + let array = new BigInt64Array(length) + this.readAlignment(buffer, 8); + for (let i = 0; i < length; i++) { + array[i] = buffer.readBigInt64(true) + } + result = array; + break; + } + case SendableStandardMessageCodec.FLOAT64_ARRAY: { + let length = this.readSize(buffer); + let array = new Float64Array(length) + this.readAlignment(buffer, 8); + for (let i = 0; i < length; i++) { + array[i] = buffer.readFloat64(true) + } + result = array; + break; + } + case SendableStandardMessageCodec.LIST: { + let length = this.readSize(buffer); + let array: Array = new Array(length) + for (let i = 0; i < length; i++) { + array[i] = this.readValue(buffer) + } + result = array; + break; + } + case SendableStandardMessageCodec.MAP: { + let size = this.readSize(buffer); + let map: Map = new Map() + for (let i = 0; i < size; i++) { + map.set(this.readValue(buffer), this.readValue(buffer)); + } + result = map; + break; + } + case SendableStandardMessageCodec.FLOAT32_ARRAY: { + let length = this.readSize(buffer); + let array = new Float32Array(length); + this.readAlignment(buffer, 4); + for (let i = 0; i < length; i++) { + array[i] = buffer.readFloat32(true) + } + result = array; + break; + } + default: + throw new Error("Message corrupted, type=" + type); + } + return result; + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStandardMethodCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStandardMethodCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..9d7de6e6025431db1c04473232ba1130d5181994 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStandardMethodCodec.ets @@ -0,0 +1,119 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { ByteBuffer } from '../../util/ByteBuffer'; +import FlutterException from './FlutterException'; +import Any from './Any'; +import MethodCall from './MethodCall'; +import SendableMethodCodec from './SendableMethodCodec'; +import SendableStandardMessageCodec from './SendableStandardMessageCodec'; + +/** + * A {@link SendableMethodCodec} using the Flutter standard binary encoding. + * + *

This codec is guaranteed to be compatible with the corresponding StandardMethodCodec + * on the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

Values supported as method arguments and result payloads are those supported by {@link + * StandardMessageCodec}. + */ +@Sendable +export default class SendableStandardMethodCodec implements SendableMethodCodec { + private static TAG: string = "SendableStandardMethodCodec"; + public static INSTANCE: SendableStandardMethodCodec = + new SendableStandardMethodCodec(SendableStandardMessageCodec.INSTANCE); + private messageCodec: SendableStandardMessageCodec; + + /** Creates a new method codec based on the specified message codec. */ + constructor(messageCodec: SendableStandardMessageCodec) { + this.messageCodec = messageCodec; + } + + encodeMethodCall(methodCall: MethodCall): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)); + this.messageCodec.writeValue(stream, methodCall.method); + this.messageCodec.writeValue(stream, methodCall.args); + return stream.buffer; + } + + decodeMethodCall(methodCall: ArrayBuffer): MethodCall { + const buffer = ByteBuffer.from(methodCall); + const method: Any = this.messageCodec.readValue(buffer); + const args: Any = this.messageCodec.readValue(buffer); + if (typeof method == 'string' && !buffer.hasRemaining()) { + return new MethodCall(method, args); + } + throw new Error("Method call corrupted"); + } + + encodeSuccessEnvelope(result: Any): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)); + stream.writeInt8(0); + this.messageCodec.writeValue(stream, result); + return stream.buffer; + } + + encodeErrorEnvelope(errorCode: string, errorMessage: string, errorDetails: Any): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)); + stream.writeInt8(1); + this.messageCodec.writeValue(stream, errorCode); + this.messageCodec.writeValue(stream, errorMessage); + if (errorDetails instanceof Error) { + this.messageCodec.writeValue(stream, errorDetails.stack); + } else { + this.messageCodec.writeValue(stream, errorDetails); + } + return stream.buffer; + } + + encodeErrorEnvelopeWithStacktrace(errorCode: string, errorMessage: string, errorDetails: Any, + errorStacktrace: string): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)); + stream.writeInt8(1); + this.messageCodec.writeValue(stream, errorCode); + this.messageCodec.writeValue(stream, errorMessage); + if (errorDetails instanceof Error) { + this.messageCodec.writeValue(stream, errorDetails.stack); + } else { + this.messageCodec.writeValue(stream, errorDetails); + } + this.messageCodec.writeValue(stream, errorStacktrace); + return stream.buffer; + } + + decodeEnvelope(envelope: ArrayBuffer): Any { + const buffer = ByteBuffer.from(envelope); + const flag = buffer.readInt8(); + switch (flag) { + case 0: { + const result: Any = this.messageCodec.readValue(buffer); + if (!buffer.hasRemaining()) { + return result; + } + // Falls through intentionally. + } + case 1: { + const code: Any = this.messageCodec.readValue(buffer); + const message: Any = this.messageCodec.readValue(buffer); + const details: Any = this.messageCodec.readValue(buffer); + if (typeof code == 'string' && (message == null || typeof message == 'string') && !buffer.hasRemaining()) { + throw new FlutterException(code, message, details); + } + } + } + throw new Error("Envelope corrupted"); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStringCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStringCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..23466ca64ae0cdeccf49a7475a8a2f96c7b1cf54 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStringCodec.ets @@ -0,0 +1,43 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import SendableMessageCodec from './SendableMessageCodec'; +import StringUtils from '../../util/StringUtils'; + +/** + * A {@link MessageCodec} using UTF-8 encoded String messages. + * + *

This codec is guaranteed to be compatible with the corresponding StringCodec on the + * Dart side. These parts of the Flutter SDK are evolved synchronously. + */ +@Sendable +export default class SendableStringCodec implements SendableMessageCodec { + static readonly INSTANCE: SendableStringCodec = new SendableStringCodec(); + + encodeMessage(message: string): ArrayBuffer { + if (message == null) { + return StringUtils.stringToArrayBuffer(""); + } + return StringUtils.stringToArrayBuffer(message); + } + + decodeMessage(message: ArrayBuffer | null): string { + if (message == null) { + return ""; + } + return StringUtils.arrayBufferToString(message); + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/StandardMessageCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/StandardMessageCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..ee67cf2be3634d5424117c59dac5ac20adf6267e --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/StandardMessageCodec.ets @@ -0,0 +1,370 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { ByteBuffer } from '../../util/ByteBuffer'; +import StringUtils from '../../util/StringUtils'; +import MessageCodec from './MessageCodec'; +import TreeMap from '@ohos.util.TreeMap'; +import HashMap from '@ohos.util.HashMap'; +import LightWeightMap from '@ohos.util.LightWeightMap'; +import PlainArray from '@ohos.util.PlainArray'; +import List from '@ohos.util.List'; +import LinkedList from '@ohos.util.LinkedList'; +import Any from './Any'; +import { ArrayList } from '@kit.ArkTS'; + +/** + * MessageCodec using the Flutter standard binary encoding. + * + *

This codec is guaranteed to be compatible with the corresponding StandardMessageCodec + * on the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

Supported messages are acyclic values of these forms: + * + *

    + *
  • null + *
  • Booleans + *
  • number + *
  • BigIntegers (see below) + *
  • Int8Array, Int32Array, Float32Array, Float64Array + *
  • Strings + *
  • Array[] + *
  • Lists of supported values + *
  • Maps with supported keys and values + *
+ * + *

On the Dart side, these values are represented as follows: + * + *

    + *
  • null: null + *
  • Boolean: bool + *
  • Byte, Short, Integer, Long: int + *
  • Float, Double: double + *
  • String: String + *
  • byte[]: Uint8List + *
  • int[]: Int32List + *
  • long[]: Int64List + *
  • float[]: Float32List + *
  • double[]: Float64List + *
  • List: List + *
  • Map: Map + *
+ * + *

BigIntegers are represented in Dart as strings with the hexadecimal representation of the + * integer's value. + * + *

To extend the codec, overwrite the writeValue and readValueOfType methods. + */ +export default class StandardMessageCodec implements MessageCodec { + private static TAG = "StandardMessageCodec#"; + static INSTANCE = new StandardMessageCodec(); + + encodeMessage(message: Any): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)) + this.writeValue(stream, message); + return stream.buffer + } + + decodeMessage(message: ArrayBuffer | null): Any { + if (message == null) { + return null + } + const buffer = ByteBuffer.from(message) + return this.readValue(buffer) + } + + private static NULL = 0; + private static TRUE = 1; + private static FALSE = 2; + private static INT32 = 3; + private static INT64 = 4; + private static BIGINT = 5; + private static FLOAT64 = 6; + private static STRING = 7; + private static UINT8_ARRAY = 8; + private static INT32_ARRAY = 9; + private static INT64_ARRAY = 10; + private static FLOAT64_ARRAY = 11; + private static LIST = 12; + private static MAP = 13; + private static FLOAT32_ARRAY = 14; + private INT64_MAX = 9223372036854775807; + private INT64_MIN = -9223372036854775808; + + writeValue(stream: ByteBuffer, value: Any): Any { + if (value == null || value == undefined) { + stream.writeInt8(StandardMessageCodec.NULL); + } else if (typeof value === "boolean") { + stream.writeInt8(value ? StandardMessageCodec.TRUE : StandardMessageCodec.FALSE) + } else if (typeof value === "number") { + if (Number.isInteger(value)) { //整型 + if (-0x7fffffff - 1 <= value && value <= 0x7fffffff) { //int32 + stream.writeInt8(StandardMessageCodec.INT32); + stream.writeInt32(value, true); + } else if (Number.MIN_SAFE_INTEGER <= value && value <= Number.MAX_SAFE_INTEGER) { //int64 number整型取值范围 + stream.writeInt8(StandardMessageCodec.INT64); + stream.writeInt64(value, true); + } else { //被判为整型的double型 + stream.writeInt8(StandardMessageCodec.FLOAT64); + this.writeAlignment(stream, 8); + stream.writeFloat64(value, true); + } + } else { //浮点型 + stream.writeInt8(StandardMessageCodec.FLOAT64); + this.writeAlignment(stream, 8); + stream.writeFloat64(value, true); + } + } else if (typeof value === "bigint") { + // https://api.flutter.dev/flutter/services/StandardMessageCodec/writeValue.html + // + // The format is first the type byte (0x05), then the actual number + // as an ASCII string giving the hexadecimal representation of the + // integer, with the string's length as encoded by writeSize + // followed by the string bytes. + if (value >= this.INT64_MIN && value <= this.INT64_MAX) { + stream.writeInt8(StandardMessageCodec.INT64); + stream.writeBigInt64(value, true); + } else { + // Convert bigint to a hexadecimal string + stream.writeInt8(StandardMessageCodec.BIGINT); + const hexString = value.toString(16); + // Map each character in the hexadecimal string to its ASCII code + const asciiString = hexString.split('').map(char => char.charCodeAt(0)); + this.writeBytes(stream, Uint8Array.from(asciiString)); + } + } else if (typeof value === "string") { + stream.writeInt8(StandardMessageCodec.STRING); + let stringBuff = StringUtils.stringToArrayBuffer(value); + this.writeBytes(stream, new Uint8Array(stringBuff)); + } else if (value instanceof Uint8Array) { + stream.writeInt8(StandardMessageCodec.UINT8_ARRAY); + this.writeBytes(stream, value) + } else if (value instanceof Int32Array) { + stream.writeInt8(StandardMessageCodec.INT32_ARRAY); + this.writeSize(stream, value.length); + this.writeAlignment(stream, 4); + value.forEach(item => stream.writeInt32(item, true)); + } else if (value instanceof BigInt64Array) { + stream.writeInt8(StandardMessageCodec.INT64_ARRAY); + this.writeSize(stream, value.length); + this.writeAlignment(stream, 8); + value.forEach(item => stream.writeBigInt64(item, true)); + } else if (value instanceof Float32Array) { + stream.writeInt8(StandardMessageCodec.FLOAT32_ARRAY); + this.writeSize(stream, value.length); + this.writeAlignment(stream, 4); + value.forEach(item => stream.writeFloat32(item, true)); + } else if (value instanceof Float64Array) { + stream.writeInt8(StandardMessageCodec.FLOAT64_ARRAY); + this.writeSize(stream, value.length); + this.writeAlignment(stream, 8); + value.forEach(item => stream.writeFloat64(item, true)); + } else if (value instanceof Array || value instanceof Int8Array || value instanceof Int16Array + || value instanceof Uint16Array || value instanceof Uint32Array || value instanceof List + || value instanceof LinkedList || value instanceof ArrayList) { + stream.writeInt8(StandardMessageCodec.LIST) + this.writeSize(stream, value.length); + value.forEach((item: Any): void => this.writeValue(stream, item)); + } else if (value instanceof Map) { + stream.writeInt8(StandardMessageCodec.MAP); + this.writeSize(stream, value.size); + value.forEach((value: Any, key: Any) => { + this.writeValue(stream, key); + this.writeValue(stream, value); + }); + } else if (value instanceof HashMap || value instanceof TreeMap || value instanceof LightWeightMap + || value instanceof PlainArray) { + stream.writeInt8(StandardMessageCodec.MAP); + this.writeSize(stream, value.length); + value.forEach((value: Any, key: Any) => { + this.writeValue(stream, key); + this.writeValue(stream, value); + }); + } else if (typeof value == 'object') { + let map: Map = new Map(); + Object.keys(value).forEach(key => { + map.set(key, value[key]); + }); + this.writeValue(stream, map); + } else { + throw new Error("Unsupported value: " + value); + stream.writeInt8(StandardMessageCodec.NULL); + } + return stream; + } + + writeAlignment(stream: ByteBuffer, alignment: number) { + let mod: number = stream.byteOffset % alignment; + if (mod != 0) { + for (let i = 0; i < alignment - mod; i++) { + stream.writeInt8(0); + } + } + } + + writeSize(stream: ByteBuffer, value: number) { + if (value < 254) { + stream.writeUint8(value); + } else if (value <= 0xffff) { + stream.writeUint8(254); + stream.writeUint16(value, true); + } else { + stream.writeUint8(255); + stream.writeUint32(value, true); + } + } + + writeBytes(stream: ByteBuffer, bytes: Uint8Array) { + this.writeSize(stream, bytes.length) + stream.writeUint8Array(bytes); + } + + readSize(buffer: ByteBuffer) { + let value = buffer.readUint8() & 0xff; + if (value < 254) { + return value; + } else if (value == 254) { + return buffer.readUint16(true); + } else { + return buffer.readUint32(true); + } + } + + readAlignment(buffer: ByteBuffer, alignment: number) { + let mod = buffer.byteOffset % alignment; + if (mod != 0) { + buffer.skip(alignment - mod); + } + } + + readValue(buffer: ByteBuffer): Any { + let type = buffer.readUint8() + return this.readValueOfType(type, buffer); + } + + readBytes(buffer: ByteBuffer): Uint8Array { + let length = this.readSize(buffer); + let bytesBuffer = new ArrayBuffer(length); + let bytes = new Uint8Array(bytesBuffer); + bytes.set(buffer.readUint8Array(length)); + return bytes; + } + + readValueOfType(type: number, buffer: ByteBuffer): Any { + let result: Any; + switch (type) { + case StandardMessageCodec.NULL: + result = null; + break; + case StandardMessageCodec.TRUE: + result = true; + break; + case StandardMessageCodec.FALSE: + result = false; + break; + case StandardMessageCodec.INT32: + result = buffer.readInt32(true); + break; + case StandardMessageCodec.INT64: + result = buffer.readInt64(true); + if (Number.MIN_SAFE_INTEGER <= result && result <= Number.MAX_SAFE_INTEGER) { + result = Number(result); + } + break; + case StandardMessageCodec.BIGINT: + let bytes: Uint8Array = this.readBytes(buffer); + // Convert the byte array to a UTF-8 encoded string + const hexString: string = String.fromCharCode(...bytes); + // Parse the string as a hexadecimal BigInt + result = BigInt(`0x${hexString}`); + break; + case StandardMessageCodec.FLOAT64: + this.readAlignment(buffer, 8); + result = buffer.readFloat64(true) + break; + case StandardMessageCodec.STRING: { + let bytes: Uint8Array = this.readBytes(buffer); + result = StringUtils.uint8ArrayToString(bytes); + break; + } + case StandardMessageCodec.UINT8_ARRAY: { + result = this.readBytes(buffer); + break; + } + case StandardMessageCodec.INT32_ARRAY: { + let length = this.readSize(buffer); + let array = new Int32Array(length) + this.readAlignment(buffer, 4); + for (let i = 0; i < length; i++) { + array[i] = buffer.readInt32(true) + } + result = array; + break; + } + case StandardMessageCodec.INT64_ARRAY: { + let length = this.readSize(buffer); + let array = new BigInt64Array(length) + this.readAlignment(buffer, 8); + for (let i = 0; i < length; i++) { + array[i] = buffer.readBigInt64(true) + } + result = array; + break; + } + case StandardMessageCodec.FLOAT64_ARRAY: { + let length = this.readSize(buffer); + let array = new Float64Array(length) + this.readAlignment(buffer, 8); + for (let i = 0; i < length; i++) { + array[i] = buffer.readFloat64(true) + } + result = array; + break; + } + case StandardMessageCodec.LIST: { + let length = this.readSize(buffer); + let array: Array = new Array(length) + for (let i = 0; i < length; i++) { + array[i] = this.readValue(buffer) + } + result = array; + break; + } + case StandardMessageCodec.MAP: { + let size = this.readSize(buffer); + let map: Map = new Map() + for (let i = 0; i < size; i++) { + map.set(this.readValue(buffer), this.readValue(buffer)); + } + result = map; + break; + } + case StandardMessageCodec.FLOAT32_ARRAY: { + let length = this.readSize(buffer); + let array = new Float32Array(length); + this.readAlignment(buffer, 4); + for (let i = 0; i < length; i++) { + array[i] = buffer.readFloat32(true) + } + result = array; + break; + } + default: + throw new Error("Message corrupted, type=" + type); + } + return result; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/StandardMethodCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/StandardMethodCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..8186d1d9deb126024b73e09b016a4937e5b16ee0 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/StandardMethodCodec.ets @@ -0,0 +1,117 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { ByteBuffer } from '../../util/ByteBuffer'; +import FlutterException from './FlutterException'; +import Any from './Any'; +import MethodCall from './MethodCall'; +import MethodCodec from './MethodCodec'; +import StandardMessageCodec from './StandardMessageCodec'; + +/** + * A {@link MethodCodec} using the Flutter standard binary encoding. + * + *

This codec is guaranteed to be compatible with the corresponding StandardMethodCodec + * on the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

Values supported as method arguments and result payloads are those supported by {@link + * StandardMessageCodec}. + */ +export default class StandardMethodCodec implements MethodCodec { + private static TAG = "StandardMethodCodec"; + public static INSTANCE = new StandardMethodCodec(StandardMessageCodec.INSTANCE); + private messageCodec: StandardMessageCodec; + + /** Creates a new method codec based on the specified message codec. */ + constructor(messageCodec: StandardMessageCodec) { + this.messageCodec = messageCodec; + } + + encodeMethodCall(methodCall: MethodCall): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)); + this.messageCodec.writeValue(stream, methodCall.method); + this.messageCodec.writeValue(stream, methodCall.args); + return stream.buffer; + } + + decodeMethodCall(methodCall: ArrayBuffer): MethodCall { + const buffer = ByteBuffer.from(methodCall); + const method: Any = this.messageCodec.readValue(buffer); + const args: Any = this.messageCodec.readValue(buffer); + if (typeof method == 'string' && !buffer.hasRemaining()) { + return new MethodCall(method, args); + } + throw new Error("Method call corrupted"); + } + + encodeSuccessEnvelope(result: Any): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)); + stream.writeInt8(0); + this.messageCodec.writeValue(stream, result); + return stream.buffer; + } + + encodeErrorEnvelope(errorCode: string, errorMessage: string, errorDetails: Any): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)); + stream.writeInt8(1); + this.messageCodec.writeValue(stream, errorCode); + this.messageCodec.writeValue(stream, errorMessage); + if (errorDetails instanceof Error) { + this.messageCodec.writeValue(stream, errorDetails.stack); + } else { + this.messageCodec.writeValue(stream, errorDetails); + } + return stream.buffer; + } + + encodeErrorEnvelopeWithStacktrace(errorCode: string, errorMessage: string, errorDetails: Any, + errorStacktrace: string): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)); + stream.writeInt8(1); + this.messageCodec.writeValue(stream, errorCode); + this.messageCodec.writeValue(stream, errorMessage); + if (errorDetails instanceof Error) { + this.messageCodec.writeValue(stream, errorDetails.stack); + } else { + this.messageCodec.writeValue(stream, errorDetails); + } + this.messageCodec.writeValue(stream, errorStacktrace); + return stream.buffer; + } + + decodeEnvelope(envelope: ArrayBuffer): Any { + const buffer = ByteBuffer.from(envelope); + const flag = buffer.readInt8(); + switch (flag) { + case 0: { + const result: Any = this.messageCodec.readValue(buffer); + if (!buffer.hasRemaining()) { + return result; + } + // Falls through intentionally. + } + case 1: { + const code: Any = this.messageCodec.readValue(buffer); + const message: Any = this.messageCodec.readValue(buffer); + const details: Any = this.messageCodec.readValue(buffer); + if (typeof code == 'string' && (message == null || typeof message == 'string') && !buffer.hasRemaining()) { + throw new FlutterException(code, message, details); + } + } + } + throw new Error("Envelope corrupted"); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/StringCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/StringCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..3ce96e365ce74d7e82bbf2f742c1bdb3800afb27 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/StringCodec.ets @@ -0,0 +1,42 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import StringUtils from '../../util/StringUtils'; +import MessageCodec from './MessageCodec'; + +/** + * A {@link MessageCodec} using UTF-8 encoded String messages. + * + *

This codec is guaranteed to be compatible with the corresponding StringCodec on the + * Dart side. These parts of the Flutter SDK are evolved synchronously. + */ +export default class StringCodec implements MessageCodec { + static readonly INSTANCE = new StringCodec(); + + encodeMessage(message: string): ArrayBuffer { + if (message == null) { + return StringUtils.stringToArrayBuffer(""); + } + return StringUtils.stringToArrayBuffer(message); + } + + decodeMessage(message: ArrayBuffer | null): string { + if (message == null) { + return ""; + } + return StringUtils.arrayBufferToString(message); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/ListenableEditingState.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/ListenableEditingState.ets new file mode 100644 index 0000000000000000000000000000000000000000..63cae8f8efa32be7aaf176fe8a8ad0fb6e1aaff9 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/ListenableEditingState.ets @@ -0,0 +1,375 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { TextEditState } from '../../embedding/engine/systemchannels/TextInputChannel'; +import Log from '../../util/Log'; +import inputMethod from '@ohos.inputMethod'; +import ArrayList from '@ohos.util.ArrayList'; +import { TextEditingDelta } from './TextEditingDelta'; +import TextInputChannel from '../../embedding/engine/systemchannels/TextInputChannel'; +import { FlutterTextUtils } from './TextUtils'; + +const TAG = "ListenableEditingState"; + +export class ListenableEditingState { + private TextInputChannel: TextInputChannel | null = null; + private client: number = 0 + //Cache used to storage software keyboard input action + private mStringCache: string; + private mSelectionStartCache: number = 0; + private mSelectionEndCache: number = 0; + private mComposingStartCache: number = 0; + private mComposingEndCache: number = 0; + //used to compare with Cache + + private mListeners: ArrayList = new ArrayList(); + private mPendingListeners: ArrayList = new ArrayList(); + private mBatchTextEditingDeltas: ArrayList = new ArrayList(); + private mChangeNotificationDepth: number = 0; + private mBatchEditNestDepth: number = 0; + private mTextWhenBeginBatchEdit: string; + private mSelectionStartWhenBeginBatchEdit: number = 0; + private mSelectionEndWhenBeginBatchEdit: number = 0; + private mComposingStartWhenBeginBatchEdit: number = 0; + private mComposingEndWhenBeginBatchEdit: number = 0; + + constructor(TextInputChannel: TextInputChannel | null, client: number) { + this.TextInputChannel = TextInputChannel; + this.client = client + this.mStringCache = ""; + this.mTextWhenBeginBatchEdit = ""; + this.mSelectionStartCache = 0; + this.mSelectionEndCache = 0; + this.mComposingStartCache = -1; + this.mComposingEndCache = -1; + } + + extractBatchTextEditingDeltas(): ArrayList { + let currentBatchDeltas = new ArrayList(); + this.mBatchTextEditingDeltas.forEach((data) => { + currentBatchDeltas.add(data); + }) + this.mBatchTextEditingDeltas.clear(); + return currentBatchDeltas; + } + + clearBatchDeltas(): void { + this.mBatchTextEditingDeltas.clear(); + } + + replace(start: number, end: number, tb: String, tbStart: number, tbEnd: number): void { + const placeIndex = + this.mSelectionStartCache < this.mSelectionEndCache ? this.mSelectionStartCache : this.mSelectionEndCache; + + this.mBatchTextEditingDeltas.add( + new TextEditingDelta( + this.mStringCache.toString(), + placeIndex + tbEnd, + placeIndex + tbEnd, + this.getComposingStart(), + this.getComposingEnd(), + start, + end + tbStart, + tb.toString() + )); + } + + getSelectionStart(): number { + return this.mSelectionStartCache; + } + + getSelectionEnd(): number { + return this.mSelectionEndCache; + } + + getComposingStart(): number { + return this.mComposingStartCache; + } + + getComposingEnd(): number { + return this.mComposingEndCache; + } + + getStringCache(): string { + return this.mStringCache; + } + + setSelectionStart(newSelectionStart: number): void { + this.mSelectionStartCache = newSelectionStart; + } + + setSelectionEnd(newSelectionEnd: number): void { + this.mSelectionEndCache = newSelectionEnd; + } + + setComposingStart(newComposingStart: number): void { + this.mComposingStartCache = newComposingStart; + } + + setComposingEnd(newComposingEnd: number): void { + this.mComposingEndCache = newComposingEnd; + } + + setStringCache(newStringCache: string): void { + this.mStringCache = newStringCache; + } + + notifyListener(listener: EditingStateWatcher, + textChanged: boolean, + selectionChanged: boolean, + composingChanged: boolean): void { + this.mChangeNotificationDepth++; + listener.didChangeEditingState(textChanged, selectionChanged, composingChanged); + this.mChangeNotificationDepth--; + } + + notifyListenersIfNeeded(textChanged: boolean, selectionChanged: boolean, composingChanged: boolean) { + if (textChanged || selectionChanged || composingChanged) { + for (const listener of this.mListeners) { + this.notifyListener(listener, textChanged, selectionChanged, composingChanged); + } + } + } + + handleInsertTextEvent(text: string): void { + let start = + this.mSelectionStartCache < this.mSelectionEndCache ? this.mSelectionStartCache : this.mSelectionEndCache; + let end = this.mSelectionStartCache > this.mSelectionEndCache ? this.mSelectionStartCache : this.mSelectionEndCache; + const length = text.length; + this.replace(start, end, text, 0, length); + + if (this.mStringCache.length == this.mSelectionStartCache) { + //Insert text one by one + let tempStr: string = this.mStringCache.substring(0, start) + text + this.mStringCache.substring(end); + this.mStringCache = tempStr; + this.setSelectionStart(this.mStringCache.length); + this.setSelectionEnd(this.mStringCache.length); + } else if (this.mStringCache.length > this.mSelectionStartCache) { + //Insert text in the middle of string + let tempStr: string = this.mStringCache.substring(0, start) + text + this.mStringCache.substring(end); + this.mStringCache = tempStr; + this.mSelectionStartCache = start + text.length; + this.mSelectionEndCache = this.mSelectionStartCache; + } + if (this.mListeners == null) { + Log.e(TAG, "mListeners is null"); + return; + } + this.notifyListenersIfNeeded(true, true, false); + } + + updateTextInputState(state: TextEditState): void { + this.beginBatchEdit(); + this.setStringCache(state.text); + if (state.hasSelection()) { + this.setSelectionStart(state.selectionStart); + this.setSelectionEnd(state.selectionEnd); + } else { + this.setSelectionStart(0); + this.setSelectionEnd(0); + } + this.endBatchEdit(); + } + + beginBatchEdit(): void { + this.mBatchEditNestDepth++; + if (this.mChangeNotificationDepth > 0) { + Log.e(TAG, "editing state should not be changed in a listener callback"); + } + if (this.mBatchEditNestDepth == 1 && !this.mListeners.isEmpty()) { + this.mTextWhenBeginBatchEdit = this.getStringCache(); + this.mSelectionStartWhenBeginBatchEdit = this.getSelectionStart(); + this.mSelectionEndWhenBeginBatchEdit = this.getSelectionEnd(); + this.mComposingStartWhenBeginBatchEdit = this.getComposingStart(); + this.mComposingEndWhenBeginBatchEdit = this.getComposingEnd(); + } + } + + endBatchEdit(): void { + if (this.mBatchEditNestDepth == 0) { + Log.e(TAG, "endBatchEdit called without a matching beginBatchEdit"); + return; + } + if (this.mBatchEditNestDepth == 1) { + Log.d(TAG, "mBatchEditNestDepth == 1"); + for (const listener of this.mPendingListeners) { + this.notifyListener(listener, true, true, true); + } + + if (!this.mListeners.isEmpty()) { + Log.d(TAG, "didFinishBatchEdit with " + this.mListeners.length + " listener(s)"); + const textChanged = !(this.mStringCache == this.mTextWhenBeginBatchEdit); + const selectionChanged = this.mSelectionStartWhenBeginBatchEdit != this.getSelectionStart() + || this.mSelectionEndWhenBeginBatchEdit != this.getSelectionEnd(); + const composingRegionChanged = this.mComposingStartWhenBeginBatchEdit != this.getComposingStart() + || this.mComposingEndWhenBeginBatchEdit != this.getComposingEnd(); + Log.d(TAG, "textChanged: " + textChanged + " selectionChanged: " + selectionChanged + + " composingRegionChanged: " + composingRegionChanged); + this.notifyListenersIfNeeded(textChanged, selectionChanged, composingRegionChanged); + } + } + for (const listener of this.mPendingListeners) { + this.mListeners.add(listener); + } + this.mPendingListeners.clear(); + this.mBatchEditNestDepth--; + } + + addEditingStateListener(listener: EditingStateWatcher): void { + if (this.mChangeNotificationDepth > 0) { + Log.e(TAG, "adding a listener " + JSON.stringify(listener) + " in a listener callback"); + } + if (this.mBatchEditNestDepth > 0) { + Log.d(TAG, "a listener was added to EditingState while a batch edit was in progress"); + this.mPendingListeners.add(listener); + } else { + this.mListeners.add(listener); + } + } + + removeEditingStateListener(listener: EditingStateWatcher): void { + if (this.mChangeNotificationDepth > 0) { + Log.e(TAG, "removing a listener " + JSON.stringify(listener) + " in a listener callback"); + } + this.mListeners.remove(listener); + if (this.mBatchEditNestDepth > 0) { + this.mPendingListeners.remove(listener); + } + } + + handleDeleteEvent(leftOrRight: boolean, length: number): void { + if (length === 0) { + return; + } + + let start = + this.mSelectionStartCache < this.mSelectionEndCache ? this.mSelectionStartCache : this.mSelectionEndCache; + let end = this.mSelectionStartCache > this.mSelectionEndCache ? this.mSelectionStartCache : this.mSelectionEndCache; + + if (leftOrRight == false) { + //delete left + if (start == 0 && end == 0) { + return; + } + + let unicodeStart = start; + if (start == end) { + for (let i = 0; i < length; i++) { + unicodeStart = FlutterTextUtils.getOffsetBefore(this.mStringCache, unicodeStart); + if (unicodeStart === 0) { + break; + } + } + } + this.replace(unicodeStart, end, "", 0, 0); + this.mSelectionStartCache = unicodeStart; + let tempStr: string = this.mStringCache.slice(0, unicodeStart) + this.mStringCache.slice(end); + this.mStringCache = tempStr; + this.mSelectionEndCache = this.mSelectionStartCache; + } else if (leftOrRight == true) { + //delete right + if (start == this.mStringCache.length) { + return; + } + let unicodeEnd = end; + if (start == end) { + for (let i = 0; i < length; i++) { + unicodeEnd = FlutterTextUtils.getOffsetAfter(this.mStringCache, unicodeEnd); + if (unicodeEnd === this.mStringCache.length) { + break; + } + } + } + this.replace(start, unicodeEnd, "", 0, 0); + this.mSelectionEndCache = start; + let tempStr: string = this.mStringCache.slice(0, start) + + (unicodeEnd >= this.mStringCache.length ? "" : this.mStringCache.slice(unicodeEnd)); + this.mStringCache = tempStr; + this.mSelectionStartCache = this.mSelectionEndCache; + } + this.notifyListenersIfNeeded(true, true, false); + } + + handleNewlineEvent(): void { + // 获取光标所在位置; + // 当光标移动前位置小于移动后的位置时,获取光标移动前位置;反之获取移动后位置 + let start = + this.mSelectionStartCache < this.mSelectionEndCache ? this.mSelectionStartCache : this.mSelectionEndCache; + // 当光标移动前位置大于移动后的位置时,获取光标移动前位置;反之获取移动后位置 + let end = this.mSelectionStartCache > this.mSelectionEndCache ? this.mSelectionStartCache : this.mSelectionEndCache; + + // 对光标位置和字符串长度进行对比,决定光标位置的计算方法 + if (this.mStringCache.length == this.mSelectionStartCache) { + //Insert newline one by one + let tempStr: string = this.mStringCache.substring(0, start) + '\n' + this.mStringCache.substring(end); + this.mStringCache = tempStr; + this.setSelectionStart(this.mStringCache.length); + this.setSelectionEnd(this.mStringCache.length); + } else if (this.mStringCache.length > this.mSelectionStartCache) { + //Insert newline in the middle of string + let tempStr: string = this.mStringCache.substring(0, start) + '\n' + this.mStringCache.substring(end); + this.mStringCache = tempStr; + this.mSelectionStartCache = start + 1; + this.mSelectionEndCache = this.mSelectionStartCache; + } + if (this.mListeners == null) { + Log.e(TAG, "mListeners is null"); + return; + } + this.notifyListenersIfNeeded(true, true, false); + } + + handleFunctionKey(functionKey: inputMethod.FunctionKey): void { + if (!this.TextInputChannel) { + return + } + switch (functionKey.enterKeyType) { + case inputMethod.EnterKeyType.PREVIOUS: + this.TextInputChannel.previous(this.client); + break; + case inputMethod.EnterKeyType.UNSPECIFIED: + this.TextInputChannel.unspecifiedAction(this.client); + break; + case inputMethod.EnterKeyType.NONE: + this.TextInputChannel.newline(this.client); + break; + case inputMethod.EnterKeyType.GO: + this.TextInputChannel.go(this.client); + break; + case inputMethod.EnterKeyType.SEARCH: + this.TextInputChannel.search(this.client); + break; + case inputMethod.EnterKeyType.SEND: + this.TextInputChannel.send(this.client); + break; + case inputMethod.EnterKeyType.NEXT: + this.TextInputChannel.next(this.client); + break; + case inputMethod.EnterKeyType.DONE: + this.TextInputChannel.done(this.client); + break; + } + } + + handleSelectByRange(range: inputMethod.Range): void { + Log.d(TAG, "handleSelectByRange start: " + range.start + " end: " + range.end); + } +} + +export interface EditingStateWatcher { + // Changing the editing state in a didChangeEditingState callback may cause unexpected + // behavior. + didChangeEditingState(textChanged: boolean, selectionChanged: boolean, composingRegionChanged: boolean): void; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/TextEditingDelta.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/TextEditingDelta.ets new file mode 100644 index 0000000000000000000000000000000000000000..281c1de5b417fdc5982a2495016c49abfae9eb5b --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/TextEditingDelta.ets @@ -0,0 +1,85 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import Log from '../../util/Log'; + +export class TextEditingDelta { + private static TAG = "TextEditingDelta"; + private oldText: string = ""; + private deltaText: string = ""; + private deltaStart: number = 0; + private deltaEnd: number = 0; + private newSelectionStart: number; + private newSelectionEnd: number; + private newComposingStart: number; + private newComposingEnd: number; + + constructor(oldEditable: string, + selectionStart: number, + selectionEnd: number, + composingStart: number, + composingEnd: number, + replacementDestinationStart?: number, + replacementDestinationEnd?: number, + replacementSource?: string) { + this.newSelectionStart = selectionStart; + this.newSelectionEnd = selectionEnd; + this.newComposingStart = composingStart; + this.newComposingEnd = composingEnd; + if (replacementDestinationStart === undefined || + replacementDestinationEnd === undefined || + replacementSource === undefined) { + this.setDeltas(oldEditable, "", -1, -1); + } else { + this.setDeltas( + oldEditable, + replacementSource, + replacementDestinationStart, + replacementDestinationEnd); + } + } + + setDeltas(oldText: string, newText: string, newStart: number, newExtent: number): void { + this.oldText = oldText; + this.deltaText = newText; + this.deltaStart = newStart; + this.deltaEnd = newExtent; + } + + toJSON(): TextEditingDeltaJson { + let state: TextEditingDeltaJson = { + oldText: this.oldText.toString(), + deltaText: this.deltaText.toString(), + deltaStart: this.deltaStart, + deltaEnd: this.deltaEnd, + selectionBase: this.newSelectionStart, + selectionExtent: this.newSelectionEnd, + composingBase: this.newComposingStart, + composingExtent: this.newComposingEnd, + }; + return state; + } +} + +export interface TextEditingDeltaJson { + oldText: string; + deltaText: string; + deltaStart: number; + deltaEnd: number; + selectionBase: number; + selectionExtent: number; + composingBase: number; + composingExtent: number; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/TextInputPlugin.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/TextInputPlugin.ets new file mode 100644 index 0000000000000000000000000000000000000000..bdedc871e87f3f13ef29ba5eb368bd6354c6c22d --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/TextInputPlugin.ets @@ -0,0 +1,402 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import TextInputChannel, { + Configuration, + TextEditState, + TextInputMethodHandler +} from '../../embedding/engine/systemchannels/TextInputChannel'; +import inputMethod from '@ohos.inputMethod'; +import Log from '../../util/Log'; +import { EditingStateWatcher, ListenableEditingState } from './ListenableEditingState'; +import Any from '../common/Any'; +import { inputDevice } from '@kit.InputKit'; +import { BusinessError } from '@kit.BasicServicesKit'; + +/// 临时规避缺少newline对应枚举问题 +const NEWLINE_KEY_TYPE: number = 8; + +export default class TextInputPlugin implements EditingStateWatcher { + private static TAG = "TextInputPlugin"; + private textInputChannel: TextInputChannel; + private mTextInputHandler: TextInputMethodHandlerImpl; + + constructor(textInputChannel: TextInputChannel, viewId: string) { + this.textInputChannel = textInputChannel; + // viewId is used for requestFocus + this.mTextInputHandler = new TextInputMethodHandlerImpl(this, viewId); + this.textInputChannel.setTextInputMethodHandler(this.mTextInputHandler); + } + + public clearTextInputClient() { + this.textInputChannel.textInputMethodHandler?.clearClient(); + } + + setTextInputEditingState(state: TextEditState) { + + } + + getEditingState() { + return this.mTextInputHandler.mEditable; + } + + didChangeEditingState(textChanged: boolean, selectionChanged: boolean, composingRegionChanged: boolean): void { + let editable = this.mTextInputHandler.mEditable; + let inputTarget = this.mTextInputHandler.inputTarget; + let configuration = this.mTextInputHandler.configuration; + if (configuration != null && configuration.enableDeltaModel) { + this.textInputChannel.updateEditingStateWithDeltas(inputTarget.id, editable.extractBatchTextEditingDeltas()); + editable.clearBatchDeltas(); + } else { + this.textInputChannel.updateEditingState(inputTarget.id, editable.getStringCache(), + editable.getSelectionStart(), editable.getSelectionEnd(), + editable.getComposingStart(), editable.getComposingEnd()) + } + } + + detach(): void { + this.mTextInputHandler.inputMethodController.detach((err) => { + if (err) { + Log.e(TextInputPlugin.TAG, "Failed to detach: " + JSON.stringify(err)); + } + }) + } + + destroy() { + // Since the Dart side no longer listens to the lifecycle, + // the keyboard must be explicitly hidden before the engine is destroyed. + this.mTextInputHandler.hide(); + this.textInputChannel.setTextInputMethodHandler(null); + } +} + +class TextInputMethodHandlerImpl implements TextInputMethodHandler { + private static TAG = "TextInputMethodHandlerImpl"; + private textConfig: inputMethod.TextConfig; + inputMethodController: inputMethod.InputMethodController; + inputTarget: InputTarget; + public configuration: Configuration | null = null; + mEditable: ListenableEditingState; + private mRestartInputPending: boolean = false; + private plugin: EditingStateWatcher | Any; + private imcFlag: boolean = false; + private keyboardStatus: inputMethod.KeyboardStatus = inputMethod.KeyboardStatus.HIDE; + private inputAttribute: inputMethod.InputAttribute = + { textInputType: inputMethod.TextInputType.TEXT, enterKeyType: inputMethod.EnterKeyType.NONE }; + private keyboardFocusState: boolean = false; + private focusViewId: string = ""; + + constructor(plugin: TextInputPlugin | Any, viewId: string) { + this.textConfig = { + inputAttribute: this.inputAttribute + }; + this.plugin = plugin; + this.mEditable = new ListenableEditingState(null, 0); + this.inputMethodController = inputMethod.getController(); + this.inputTarget = new InputTarget(Type.NO_TARGET, 0); + this.focusViewId = viewId; + } + + /// 通过判断是否是TextInputType.none来决定是否弹出键盘 + show(): void { + if (this.canShowTextInput()) { + // Ensure the Xcomponent gains focus before the soft keyboard is displayed. + focusControl.requestFocus(this.focusViewId); + this.keyboardFocusState = true; + this.showTextInput(); + } else { + this.hide(); + } + } + + hide(): void { + // Ensure the Xcomponent loses focus before the soft keyboard is hided. + focusControl.requestFocus("unfocus-xcomponent-node"); + this.keyboardFocusState = false; + this.hideTextInput(); + } + + requestAutofill(): void { + + } + + finishAutofillContext(shouldSave: boolean): void { + + } + + setClient(textInputClientId: number, configuration: Configuration | null): void { + Log.d(TextInputMethodHandlerImpl.TAG, "textInputClientId: " + textInputClientId); + this.setTextInputClient(textInputClientId, configuration); + } + + setPlatformViewClient(id: number, usesVirtualDisplay: boolean): void { + + } + + setEditableSizeAndTransform(width: number, height: number, transform: number[]): void { + + } + + setCursorSizeAndPosition(cursorInfo: inputMethod.CursorInfo) { + try { + this.inputMethodController.updateCursor(cursorInfo, (err: BusinessError) => { + if (err) { + Log.e(TextInputMethodHandlerImpl.TAG, "Failed to updateCursor:" + JSON.stringify(err)); + return; + } + }) + } catch (err) { + Log.e(TextInputMethodHandlerImpl.TAG, "Failed to updateCursor:" + JSON.stringify(err)); + } + } + + setEditingState(editingState: TextEditState): void { + Log.d(TextInputMethodHandlerImpl.TAG, + "text:" + editingState.text + " selectionStart:" + editingState.selectionStart + " selectionEnd:" + + editingState.selectionEnd + " composingStart:" + editingState.composingStart + " composingEnd" + + editingState.composingEnd); + this.mEditable.updateTextInputState(editingState); + } + + clearClient(): void { + this.clearTextInputClient(); + } + + private async showTextInput(): Promise { + if (this.keyboardStatus == inputMethod.KeyboardStatus.SHOW) { + return; + } + await this.attach(true); + if (!this.imcFlag) { + this.listenKeyBoardEvent(); + } + } + + private async hideTextInput(): Promise { + await this.inputMethodController.detach().then(() => { + this.keyboardStatus = inputMethod.KeyboardStatus.HIDE; + this.cancelListenKeyBoardEvent(); + }).catch((err: BusinessError) => { + Log.e(TextInputMethodHandlerImpl.TAG, "Failed to detach: " + JSON.stringify(err)); + this.keyboardStatus = inputMethod.KeyboardStatus.NONE; + }); + } + + async attach(showKeyboard: boolean): Promise { + try { + await this.inputMethodController.attach(showKeyboard, this.textConfig); + this.keyboardStatus = inputMethod.KeyboardStatus.SHOW; + } catch (err) { + Log.e(TextInputMethodHandlerImpl.TAG, "Failed to attach:" + JSON.stringify(err)); + this.keyboardStatus = inputMethod.KeyboardStatus.NONE; + } + } + + handleChangeFocus(focusState: boolean) { + if (focusState && this.keyboardFocusState) { + // When the app loses focus, the system automatically detaches the input method. + // Upon regaining focus, if the input method should be displayed, it must be reattached. + this.show(); + } + try { + inputDevice.getDeviceList((Error: Error, ids: Array) => { + let isPhysicalKeyboard = false; + for (let i = 0; i < ids.length; i++) { + const type = inputDevice.getKeyboardTypeSync(ids[i]); + if (type == inputDevice.KeyboardType.ALPHABETIC_KEYBOARD || type == inputDevice.KeyboardType.DIGITAL_KEYBOARD) { + isPhysicalKeyboard = true; + break; + } + } + + if(focusState && isPhysicalKeyboard && this.keyboardFocusState) { + this.cancelListenKeyBoardEvent(); + this.inputMethodController.detach().then(async () =>{ + await this.attach(true); + this.listenKeyBoardEvent(); + }) + } + }) + } catch (error) { + Log.e(TextInputMethodHandlerImpl.TAG, `Failed to query device. Code is ${error.code}, message is ${error.message}`) + } + } + + async updateAttribute(): Promise { + if (this.keyboardStatus != inputMethod.KeyboardStatus.SHOW) { + return; + } + try { + await this.inputMethodController.updateAttribute(this.inputAttribute); + } catch (err) { + Log.e(TextInputMethodHandlerImpl.TAG, "Failed to updateAttribute:" + JSON.stringify(err)); + } + } + + setTextInputClient(client: number, configuration: Configuration | null): void { + if (configuration) { + this.configuration = configuration; + if (configuration.inputType) { + this.textConfig.inputAttribute.textInputType = configuration.inputType.type; + this.textConfig.inputAttribute.enterKeyType = configuration.inputAction as Any; + } + } + if (this.canShowTextInput()) { + this.inputTarget = new InputTarget(Type.FRAMEWORK_CLIENT, client); + } else { + this.inputTarget = new InputTarget(Type.NO_TARGET, client); + } + this.mEditable.removeEditingStateListener(this.plugin); + + this.mEditable = new ListenableEditingState(this.plugin.textInputChannel, this.inputTarget.id); + + this.mRestartInputPending = true; + this.mEditable.addEditingStateListener(this.plugin); + + this.inputAttribute = this.textConfig.inputAttribute; + + this.updateAttribute(); + } + + canShowTextInput(): boolean { + if (this.configuration == null || this.configuration.inputType == null) { + return true; + } + return this.configuration.inputType.type != inputMethod.TextInputType.NONE; + } + + listenKeyBoardEvent(): void { + try { + this.inputMethodController.on('insertText', this.insertTextCallback); + } catch (err) { + Log.e(TextInputMethodHandlerImpl.TAG, "Failed to subscribe insertText:" + JSON.stringify(err)); + this.cancelListenKeyBoardEvent(); + return; + } + + try { + this.inputMethodController.on('deleteLeft', this.deleteLeftCallback) + } catch (err) { + Log.e(TextInputMethodHandlerImpl.TAG, "Failed to subscribe deleteLeft:" + JSON.stringify(err)); + this.cancelListenKeyBoardEvent(); + return; + } + + try { + this.inputMethodController.on('deleteRight', this.deleteRightCallback) + } catch (err) { + Log.e(TextInputMethodHandlerImpl.TAG, "Failed to subscribe deleteRight:" + JSON.stringify(err)); + this.cancelListenKeyBoardEvent(); + return; + } + + try { + this.inputMethodController.on('sendFunctionKey', this.sendFunctionKeyCallback) + } catch (err) { + Log.e(TextInputMethodHandlerImpl.TAG, "Failed to subscribe sendFunctionKey:" + JSON.stringify(err)); + this.cancelListenKeyBoardEvent(); + return; + } + + try { + this.inputMethodController.on('sendKeyboardStatus', this.sendKeyboardStatusCallback) + } catch (err) { + Log.e(TextInputMethodHandlerImpl.TAG, "Failed to subscribe sendKeyboardStatus:" + JSON.stringify(err)); + this.cancelListenKeyBoardEvent(); + return; + } + + try { + this.inputMethodController.on('selectByRange', this.selectByRangeCallback) + } catch (err) { + Log.e(TextInputMethodHandlerImpl.TAG, "Failed to subscribe selectByRange:" + JSON.stringify(err)); + this.cancelListenKeyBoardEvent(); + return; + } + Log.d(TextInputMethodHandlerImpl.TAG, "listenKeyBoardEvent success"); + this.imcFlag = true; + } + + private insertTextCallback = (text: string) => { + Log.d(TextInputMethodHandlerImpl.TAG, "insertText: " + text); + this.mEditable.handleInsertTextEvent(text); + } + + private deleteLeftCallback = (length: number) => { + this.mEditable.handleDeleteEvent(false, length); + } + + private deleteRightCallback = (length: number) => { + this.mEditable.handleDeleteEvent(true, length); + } + + private sendFunctionKeyCallback = (functionKey: inputMethod.FunctionKey) => { + /// 临时规避缺少newline对应枚举类型问题 + if (functionKey.enterKeyType == NEWLINE_KEY_TYPE) { + this.mEditable.handleNewlineEvent(); + } + this.mEditable.handleFunctionKey(functionKey); + } + + private sendKeyboardStatusCallback = (state: inputMethod.KeyboardStatus) => { + this.keyboardStatus = state; + if (state == inputMethod.KeyboardStatus.HIDE) { + this.plugin.textInputChannel.onConnectionClosed(this.inputTarget.id); + } + } + + private selectByRangeCallback = (range: inputMethod.Range) => { + this.mEditable.handleSelectByRange(range); + } + + cancelListenKeyBoardEvent(): void { + this.inputMethodController?.off('insertText', this.insertTextCallback); + this.inputMethodController?.off('deleteLeft', this.deleteLeftCallback); + this.inputMethodController?.off('deleteRight', this.deleteRightCallback); + this.inputMethodController?.off('sendFunctionKey', this.sendFunctionKeyCallback); + this.inputMethodController?.off('sendKeyboardStatus', this.sendKeyboardStatusCallback); + this.inputMethodController?.off('selectByRange', this.selectByRangeCallback); + this.imcFlag = false; + } + + public clearTextInputClient(): void { + if (this.inputTarget.type == Type.VIRTUAL_DISPLAY_PLATFORM_VIEW) { + return; + } + this.mEditable.removeEditingStateListener(this.plugin); + this.configuration = null; + this.inputTarget = new InputTarget(Type.NO_TARGET, 0); + } +} + +enum Type { + NO_TARGET, + // InputConnection is managed by the TextInputPlugin, and events are forwarded to the Flutter + // framework. + FRAMEWORK_CLIENT, + // InputConnection is managed by a platform view that is presented on a virtual display. + VIRTUAL_DISPLAY_PLATFORM_VIEW, + PHYSICAL_DISPLAY_PLATFORM_VIEW, +} + +export class InputTarget { + type: Type; + id: number; + + constructor(type: Type, id: number) { + this.type = type; + this.id = id; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/TextUtils.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/TextUtils.ets new file mode 100644 index 0000000000000000000000000000000000000000..749a6cb6fcb96b2ed102ef75fa4b301505328aae --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/TextUtils.ets @@ -0,0 +1,357 @@ +import FlutterNapi from '../../embedding/engine/FlutterNapi'; +import Log from '../../util/Log'; + +const LINE_FEED: number = 0x0A; +const CARRIAGE_RETURN: number = 0x0D; +const COMBINING_ENCLOSING_KEYCAP: number = 0x20E3; +const CANCEL_TAG: number = 0xE007F; +const ZERO_WIDTH_JOINER: number = 0x200D; + +const TAG = "TextUtils"; + +export class FlutterTextUtils { + + static isEmoji(code: number): boolean { + return FlutterNapi.unicodeIsEmoji(code); + } + + static isEmojiModifier(code: number): boolean { + return FlutterNapi.unicodeIsEmojiModifier(code); + } + + static isEmojiModifierBase(code: number): boolean { + return FlutterNapi.unicodeIsEmojiModifierBase(code); + } + + static isVariationSelector(code: number): boolean { + return FlutterNapi.unicodeIsVariationSelector(code); + } + + static isRegionalIndicatorSymbol(code: number): boolean { + return FlutterNapi.unicodeIsRegionalIndicatorSymbol(code); + } + + static isTagSpecChar(code: number): boolean { + return 0xE0020 <= code && code <= 0xE007E; + } + + static isKeycapBase(code: number): boolean { + return ('0'.charCodeAt(0) <= code && code <= '9'.charCodeAt(0)) || code == '#'.charCodeAt(0) || code == '*'.charCodeAt(0); + } + + static codePointBefore(text: string, offset: number): number { + if (offset <= 0 || offset > text.length) { + throw new RangeError('Offset out of range'); + } + + // Get the character before the offset + const char = text[offset - 1]; + + // Check if it is a low surrogate (part of a surrogate pair) + if (offset > 1 && char >= '\uDC00' && char <= '\uDFFF') { + const prevChar = text[offset - 2]; + // Check if the previous character is a high surrogate + if (prevChar >= '\uD800' && prevChar <= '\uDBFF') { + // If it is, combine the surrogate pair into a full Unicode code point + return (prevChar.charCodeAt(0) - 0xD800) * 0x400 + (char.charCodeAt(0) - 0xDC00) + 0x10000; + } + } + + // Return the code point of the single character (if it's not a surrogate pair) + return char.charCodeAt(0); + } + + static codePointAt(text: string, offset: number): number { + if (offset >= text.length) { + throw new RangeError('Offset out of range'); + } + let char = text[offset]; + + // Check if it is a high surrogate (part of a surrogate pair) + if (char >= '\uD800' && char <= '\uDBFF' && offset + 1 < text.length) { + const nextChar = text[offset + 1]; + // Check if the previous character is a low surrogate + if (nextChar >= '\uDC00' && nextChar <= '\uDFFF') { + // If it is, combine the surrogate pair into a full Unicode code point + return (char.charCodeAt(0) - 0xD800) * 0x400 + (nextChar.charCodeAt(0) - 0xDC00) + 0x10000; + } + } + return char.charCodeAt(0); + } + + static charCount(codePoint: number): number { + // If the code point is in the BMP range (0x0000 - 0xFFFF), it needs 1 UTF-16 code unit + if (codePoint <= 0xFFFF) { + return 1; + } + // If the code point is in the supplementary range (0x10000 - 0x10FFFF), it needs 2 UTF-16 code units + return 2; + } + + static getOffsetBefore(text: string, offset: number): number { + if (offset <= 1) { + return 0; + } + + let codePoint: number = FlutterTextUtils.codePointBefore(text, offset); + let deleteCharCount: number = FlutterTextUtils.charCount(codePoint); + let lastOffset: number = offset - deleteCharCount; + + if (lastOffset == 0) { + return 0; + } + + // Line Feed + if (codePoint == LINE_FEED) { + codePoint = FlutterTextUtils.codePointBefore(text, lastOffset); + if (codePoint == CARRIAGE_RETURN) { + ++deleteCharCount; + } + return offset - deleteCharCount; + } + + // Flags + if (FlutterTextUtils.isRegionalIndicatorSymbol(codePoint)) { + codePoint = FlutterTextUtils.codePointBefore(text, lastOffset); + lastOffset -= FlutterTextUtils.charCount(codePoint); + let regionalIndicatorSymbolCount: number = 1; + while (lastOffset > 0 && FlutterTextUtils.isRegionalIndicatorSymbol(codePoint)) { + codePoint = FlutterTextUtils.codePointBefore(text, lastOffset); + lastOffset -= FlutterTextUtils.charCount(codePoint); + regionalIndicatorSymbolCount++; + } + if (FlutterTextUtils.isRegionalIndicatorSymbol(codePoint)) { + regionalIndicatorSymbolCount++; + } + if (regionalIndicatorSymbolCount % 2 == 0) { + deleteCharCount += 2; + } + return offset - deleteCharCount; + } + + // Keycaps + if (codePoint == COMBINING_ENCLOSING_KEYCAP) { + codePoint = FlutterTextUtils.codePointBefore(text, lastOffset); + lastOffset -= FlutterTextUtils.charCount(codePoint); + if (lastOffset > 0 && FlutterTextUtils.isVariationSelector(codePoint)) { + let tmpCodePoint: number = FlutterTextUtils.codePointBefore(text, lastOffset); + if (FlutterTextUtils.isKeycapBase(tmpCodePoint)) { + deleteCharCount += FlutterTextUtils.charCount(codePoint) + FlutterTextUtils.charCount(tmpCodePoint); + } + } else if (FlutterTextUtils.isKeycapBase(codePoint)) { + deleteCharCount += FlutterTextUtils.charCount(codePoint); + } + return offset - deleteCharCount; + } + + /** + * Following if statements for Emoji tag sequence and Variation selector are skipping these + * modifiers for going through the last statement that is for handling emojis. They return the + * offset if they don't find proper base characters + */ + // Emoji Tag Sequence + if (codePoint == CANCEL_TAG) { // tag_end + codePoint = FlutterTextUtils.codePointBefore(text, lastOffset); + lastOffset -= FlutterTextUtils.charCount(codePoint); + while (lastOffset > 0 && FlutterTextUtils.isTagSpecChar(codePoint)) { // tag_spec + deleteCharCount += FlutterTextUtils.charCount(codePoint); + codePoint = FlutterTextUtils.codePointBefore(text, lastOffset); + lastOffset -= FlutterTextUtils.charCount(codePoint); + } + if (!FlutterTextUtils.isEmoji(codePoint)) { // tag_base not found. Just delete the end. + return offset - 2; + } + deleteCharCount += FlutterTextUtils.charCount(codePoint); + } + + if (FlutterTextUtils.isVariationSelector(codePoint)) { + codePoint = FlutterTextUtils.codePointBefore(text, lastOffset); + if (!FlutterTextUtils.isEmoji(codePoint)) { + return offset - deleteCharCount; + } + deleteCharCount += FlutterTextUtils.charCount(codePoint); + + lastOffset -= FlutterTextUtils.charCount(codePoint); + } + + if (FlutterTextUtils.isEmoji(codePoint)) { + let isZwj: boolean = false; + let lastSeenVariantSelectorCharCount: number = 0; + do { + if (isZwj) { + deleteCharCount += FlutterTextUtils.charCount(codePoint) + lastSeenVariantSelectorCharCount + 1; + isZwj = false; + } + lastSeenVariantSelectorCharCount = 0; + if (FlutterTextUtils.isEmojiModifier(codePoint)) { + codePoint = FlutterTextUtils.codePointBefore(text, lastOffset); + lastOffset -= FlutterTextUtils.charCount(codePoint); + if (lastOffset > 0 && FlutterTextUtils.isVariationSelector(codePoint)) { + codePoint = FlutterTextUtils.codePointBefore(text, lastOffset); + if (!FlutterTextUtils.isEmoji(codePoint)) { + return offset - deleteCharCount; + } + lastSeenVariantSelectorCharCount = FlutterTextUtils.charCount(codePoint); + lastOffset -= FlutterTextUtils.charCount(codePoint); + } + if (FlutterTextUtils.isEmojiModifierBase(codePoint)) { + deleteCharCount += lastSeenVariantSelectorCharCount + FlutterTextUtils.charCount(codePoint); + } + break; + } + + if (lastOffset > 0) { + codePoint = FlutterTextUtils.codePointBefore(text, lastOffset); + lastOffset -= FlutterTextUtils.charCount(codePoint); + if (codePoint == ZERO_WIDTH_JOINER) { + isZwj = true; + codePoint = FlutterTextUtils.codePointBefore(text, lastOffset); + lastOffset -= FlutterTextUtils.charCount(codePoint); + if (lastOffset > 0 && FlutterTextUtils.isVariationSelector(codePoint)) { + codePoint = FlutterTextUtils.codePointBefore(text, lastOffset); + lastSeenVariantSelectorCharCount = FlutterTextUtils.charCount(codePoint); + lastOffset -= FlutterTextUtils.charCount(codePoint); + } + } + } + + if (lastOffset == 0) { + break; + } + } while (isZwj && FlutterTextUtils.isEmoji(codePoint)); + + if (isZwj && lastOffset == 0) { + deleteCharCount += FlutterTextUtils.charCount(codePoint) + lastSeenVariantSelectorCharCount + 1; + isZwj = false; + } + } + + return offset - deleteCharCount; + } + + static getOffsetAfter(text: string, offset: number): number { + const len = text.length; + if (offset >= len - 1) { + return len; + } + + let codePoint: number = FlutterTextUtils.codePointAt(text, offset); + let nextCharCount: number = FlutterTextUtils.charCount(codePoint); + let nextOffset: number = offset + nextCharCount; + + if (nextOffset == 0) { + return 0; + } + // Line Feed + if (codePoint == LINE_FEED) { + codePoint = FlutterTextUtils.codePointAt(text, nextOffset); + if (codePoint == CARRIAGE_RETURN) { + ++nextCharCount; + } + return offset + nextCharCount; + } + + // Flags + if (FlutterTextUtils.isRegionalIndicatorSymbol(codePoint)) { + if (nextOffset >= len - 1 + || !FlutterTextUtils.isRegionalIndicatorSymbol(FlutterTextUtils.codePointAt(text, nextOffset))) { + return offset + nextCharCount; + } + // In this case there are at least two regional indicator symbols ahead of + // offset. If those two regional indicator symbols are a pair that + // represent a region together, the next offset should be after both of + // them. + let regionalIndicatorSymbolCount: number = 0; + let regionOffset: number = offset; + while (regionOffset > 0 + && FlutterTextUtils.isRegionalIndicatorSymbol(FlutterTextUtils.codePointBefore(text, regionOffset))) { + regionOffset -= FlutterTextUtils.charCount(FlutterTextUtils.codePointBefore(text, regionOffset)); + regionalIndicatorSymbolCount++; + } + if (regionalIndicatorSymbolCount % 2 == 0) { + nextCharCount += 2; + } + return offset + nextCharCount; + } + + // Keycaps + if (FlutterTextUtils.isKeycapBase(codePoint)) { + nextCharCount += FlutterTextUtils.charCount(codePoint); + } + if (codePoint == COMBINING_ENCLOSING_KEYCAP) { + codePoint = FlutterTextUtils.codePointBefore(text, nextOffset); + nextOffset += FlutterTextUtils.charCount(codePoint); + if (nextOffset < len && FlutterTextUtils.isVariationSelector(codePoint)) { + let tmpCodePoint: number = FlutterTextUtils.codePointAt(text, nextOffset); + if (FlutterTextUtils.isKeycapBase(tmpCodePoint)) { + nextCharCount += FlutterTextUtils.charCount(codePoint) + FlutterTextUtils.charCount(tmpCodePoint); + } + } else if (FlutterTextUtils.isKeycapBase(codePoint)) { + nextCharCount += FlutterTextUtils.charCount(codePoint); + } + return offset + nextCharCount; + } + + if (FlutterTextUtils.isEmoji(codePoint)) { + let isZwj: boolean = false; + let lastSeenVariantSelectorCharCount: number = 0; + do { + if (isZwj) { + nextCharCount += FlutterTextUtils.charCount(codePoint) + lastSeenVariantSelectorCharCount + 1; + isZwj = false; + } + lastSeenVariantSelectorCharCount = 0; + if (FlutterTextUtils.isEmojiModifier(codePoint)) { + break; + } + + if (nextOffset < len) { + codePoint = FlutterTextUtils.codePointAt(text, nextOffset); + nextOffset += FlutterTextUtils.charCount(codePoint); + if (codePoint == COMBINING_ENCLOSING_KEYCAP) { + codePoint = FlutterTextUtils.codePointBefore(text, nextOffset); + nextOffset += FlutterTextUtils.charCount(codePoint); + if (nextOffset < len && FlutterTextUtils.isVariationSelector(codePoint)) { + let tmpCodePoint: number = FlutterTextUtils.codePointAt(text, nextOffset); + if (FlutterTextUtils.isKeycapBase(tmpCodePoint)) { + nextCharCount += FlutterTextUtils.charCount(codePoint) + FlutterTextUtils.charCount(tmpCodePoint); + } + } else if (FlutterTextUtils.isKeycapBase(codePoint)) { + nextCharCount += FlutterTextUtils.charCount(codePoint); + } + return offset + nextCharCount; + } + if (FlutterTextUtils.isEmojiModifier(codePoint)) { + nextCharCount += lastSeenVariantSelectorCharCount + FlutterTextUtils.charCount(codePoint); + break; + } + if (FlutterTextUtils.isVariationSelector(codePoint)) { + nextCharCount += lastSeenVariantSelectorCharCount + FlutterTextUtils.charCount(codePoint); + break; + } + if (codePoint == ZERO_WIDTH_JOINER) { + isZwj = true; + codePoint = FlutterTextUtils.codePointAt(text, nextOffset); + nextOffset += FlutterTextUtils.charCount(codePoint); + if (nextOffset < len && FlutterTextUtils.isVariationSelector(codePoint)) { + codePoint = FlutterTextUtils.codePointAt(text, nextOffset); + lastSeenVariantSelectorCharCount = FlutterTextUtils.charCount(codePoint); + nextOffset += FlutterTextUtils.charCount(codePoint); + } + } + } + + if (nextOffset >= len) { + break; + } + } while (isZwj && FlutterTextUtils.isEmoji(codePoint)); + + if (isZwj && nextOffset >= len) { + nextCharCount += FlutterTextUtils.charCount(codePoint) + lastSeenVariantSelectorCharCount + 1; + isZwj = false; + } + } + + return offset + nextCharCount; + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/localization/LocalizationPlugin.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/localization/LocalizationPlugin.ets new file mode 100644 index 0000000000000000000000000000000000000000..05421fc41e6c74003556ed92dffc9018314b7926 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/localization/LocalizationPlugin.ets @@ -0,0 +1,97 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import LocalizationChannel, { + LocalizationMessageHandler +} from '../../embedding/engine/systemchannels/LocalizationChannel' +import common from '@ohos.app.ability.common'; +import intl from '@ohos.intl'; +import Log from '../../util/Log'; +import i18n from '@ohos.i18n'; + +const TAG = "LocalizationPlugin"; + +export default class LocalizationPlugin { + private localizationChannel: LocalizationChannel; + private context: common.Context; + + localeFromString(localeString: string): intl.Locale { + localeString = localeString.replace('_', '-'); + let parts: string[] = localeString.split('-', -1); + let languageCode = parts[0]; + let scriptCode = ""; + let countryCode = ""; + let index: number = 1; + + if (parts.length > index && parts[index].length == 4) { + scriptCode = parts[index]; + index++; + } + + if (parts.length > index && parts[index].length >= 2 && parts[index].length <= 3) { + countryCode = parts[index]; + index++; + } + return new intl.Locale(languageCode + '-' + countryCode + '-' + scriptCode); + } + + private localizationMessageHandler: LocalizationMessageHandler = + new enterGetStringResource((key: string, localeString: string | null) => { + + Log.i(TAG, "getStringResource,key: " + key + ",localeString: " + localeString); + let localContext: common.Context = this.context; + let stringToReturn: string | null = null; + // 获取资源管理器 + let resMgr = localContext.resourceManager; + + try { + // 如果localeString不为空,则更新为指定地区的资源管理器 + if (localeString) { + let overrideConfig = resMgr.getOverrideConfiguration(); + overrideConfig.locale = localeString; + let overrideResMgr = resMgr.getOverrideResourceManager(overrideConfig); + stringToReturn = overrideResMgr.getStringByNameSync(key); + } else { + stringToReturn = resMgr.getStringByNameSync(key); + } + } catch (e) { + Log.e(TAG, e); + return null; + } + + return stringToReturn; + }) + + constructor(context: common.Context, localizationChannel: LocalizationChannel) { + this.context = context; + this.localizationChannel = localizationChannel; + this.localizationChannel.setLocalizationMessageHandler(this.localizationMessageHandler); + } + + sendLocaleToFlutter(): void { + let systemLocale: string = i18n.System.getSystemLocale(); + let data: Array = []; + data.push(systemLocale); + this.localizationChannel.sendLocales(data); + } +} + +class enterGetStringResource { + getStringResource: (key: string, localeString: string | null) => string | null + + constructor(getStringResource: (key: string, localeString: string | null) => string | null) { + this.getStringResource = getStringResource + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/mouse/MouseCursorPlugin.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/mouse/MouseCursorPlugin.ets new file mode 100644 index 0000000000000000000000000000000000000000..8df980b502b0de02556e5f79a5945c3852004571 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/mouse/MouseCursorPlugin.ets @@ -0,0 +1,131 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import MouseCursorChannel, { MouseCursorMethodHandler } from '../../embedding/engine/systemchannels/MouseCursorChannel'; +import pointer from '@ohos.multimodalInput.pointer'; +import HashMap from '@ohos.util.HashMap'; +import Log from '../../util/Log'; +import Any from '../common/Any'; + +const TAG: string = "MouseCursorPlugin"; + +export default class MouseCursorPlugin implements MouseCursorMethodHandler { + private mouseCursorChannel: MouseCursorChannel; + private systemCursorConstants: HashMap | null = null; + private windowId: number; + + constructor(windowId: number, mouseCursorChannel: MouseCursorChannel) { + this.windowId = windowId; + this.mouseCursorChannel = mouseCursorChannel; + this.mouseCursorChannel.setMethodHandler(this); + } + + activateSystemCursor(kind: string): void { + if (this.windowId < 0) { + Log.w(TAG, "set point style failed windowId is invalid"); + return; + } + let pointStyle: pointer.PointerStyle = this.resolveSystemCursor(kind); + try { + pointer.setPointerStyle(this.windowId, pointStyle, (err: Any) => { + Log.i(TAG, "set point style success kind : " + kind); + }) + } catch (e) { + Log.e(TAG, "set point style failed : " + kind + " " + JSON.stringify(e)); + } + } + + /** + * Return mouse cursor point style + * + *

This method guarantees to return a non-null object. + * + * @param kind mouse cursor type + * @returns point style + */ + private resolveSystemCursor(kind: string): pointer.PointerStyle { + if (this.systemCursorConstants == null) { + this.systemCursorConstants = new HashMap(); + this.systemCursorConstants.set("alias", pointer.PointerStyle.DEFAULT); + this.systemCursorConstants.set("allScroll", pointer.PointerStyle.MOVE); + this.systemCursorConstants.set("basic", pointer.PointerStyle.DEFAULT); + this.systemCursorConstants.set("cell", pointer.PointerStyle.DEFAULT); + this.systemCursorConstants.set("click", pointer.PointerStyle.HAND_POINTING); + this.systemCursorConstants.set("contextMenu", pointer.PointerStyle.DEFAULT); + this.systemCursorConstants.set("copy", pointer.PointerStyle.CURSOR_COPY); + this.systemCursorConstants.set("forbidden", pointer.PointerStyle.CURSOR_FORBID); + this.systemCursorConstants.set("grab", pointer.PointerStyle.HAND_OPEN); + this.systemCursorConstants.set("grabbing", pointer.PointerStyle.HAND_GRABBING); + this.systemCursorConstants.set("help", pointer.PointerStyle.HELP); + this.systemCursorConstants.set("move", pointer.PointerStyle.MOVE); + this.systemCursorConstants.set("none", pointer.PointerStyle.DEFAULT); + this.systemCursorConstants.set("noDrop", pointer.PointerStyle.DEFAULT); + this.systemCursorConstants.set("precise", pointer.PointerStyle.CROSS); + this.systemCursorConstants.set("text", pointer.PointerStyle.TEXT_CURSOR); + this.systemCursorConstants.set("resizeColum", pointer.PointerStyle.NORTH_SOUTH); + this.systemCursorConstants.set("resizeDown", pointer.PointerStyle.SOUTH); + this.systemCursorConstants.set("resizeDownLeft", pointer.PointerStyle.SOUTH_WEST); + this.systemCursorConstants.set("resizeDownRight", pointer.PointerStyle.SOUTH_EAST); + this.systemCursorConstants.set("resizeLeft", pointer.PointerStyle.WEST); + this.systemCursorConstants.set("resizeLeftRight", pointer.PointerStyle.RESIZE_LEFT_RIGHT); + this.systemCursorConstants.set("resizeRight", pointer.PointerStyle.EAST); + this.systemCursorConstants.set("resizeRow", pointer.PointerStyle.WEST_EAST); + this.systemCursorConstants.set("resizeUp", pointer.PointerStyle.NORTH); + this.systemCursorConstants.set("resizeUpDown", pointer.PointerStyle.RESIZE_UP_DOWN); + this.systemCursorConstants.set("resizeUpLeft", pointer.PointerStyle.NORTH_WEST); + this.systemCursorConstants.set("resizeUpRight", pointer.PointerStyle.NORTH_EAST); + this.systemCursorConstants.set("resizeUpLeftDownRight", pointer.PointerStyle.NORTH_WEST_SOUTH_EAST); + this.systemCursorConstants.set("resizeUpRightDownLeft", pointer.PointerStyle.NORTH_EAST_SOUTH_WEST); + this.systemCursorConstants.set("verticalText", pointer.PointerStyle.TEXT_CURSOR); + this.systemCursorConstants.set("wait", pointer.PointerStyle.DEFAULT); + this.systemCursorConstants.set("zoomIn", pointer.PointerStyle.ZOOM_IN); + this.systemCursorConstants.set("zoomOut", pointer.PointerStyle.ZOOM_OUT); + this.systemCursorConstants.set("middleBtnEast", pointer.PointerStyle.MIDDLE_BTN_EAST); + this.systemCursorConstants.set("middleBtnWest", pointer.PointerStyle.MIDDLE_BTN_WEST); + this.systemCursorConstants.set("middleBtnSouth", pointer.PointerStyle.MIDDLE_BTN_SOUTH); + this.systemCursorConstants.set("middleBtnNorth", pointer.PointerStyle.MIDDLE_BTN_NORTH); + this.systemCursorConstants.set("middleBtnNorthSouth", pointer.PointerStyle.MIDDLE_BTN_NORTH_SOUTH); + this.systemCursorConstants.set("middleBtnNorthEast", pointer.PointerStyle.MIDDLE_BTN_NORTH_EAST); + this.systemCursorConstants.set("middleBtnNorthWest", pointer.PointerStyle.MIDDLE_BTN_NORTH_WEST); + this.systemCursorConstants.set("middleBtnSouthEast", pointer.PointerStyle.MIDDLE_BTN_SOUTH_EAST); + this.systemCursorConstants.set("middleBtnSouthWest", pointer.PointerStyle.MIDDLE_BTN_SOUTH_WEST); + this.systemCursorConstants.set("middleBtnNorthSouthWestEast", + pointer.PointerStyle.MIDDLE_BTN_NORTH_SOUTH_WEST_EAST); + this.systemCursorConstants.set("horizontalTextCursor", pointer.PointerStyle.HORIZONTAL_TEXT_CURSOR); + this.systemCursorConstants.set("cursorCross", pointer.PointerStyle.CURSOR_CROSS); + this.systemCursorConstants.set("cursorCircle", pointer.PointerStyle.CURSOR_CIRCLE); + this.systemCursorConstants.set("loading", pointer.PointerStyle.LOADING); + this.systemCursorConstants.set("running", pointer.PointerStyle.RUNNING); + this.systemCursorConstants.set("colorSucker", pointer.PointerStyle.COLOR_SUCKER); + this.systemCursorConstants.set("screenshotChoose", pointer.PointerStyle.SCREENSHOT_CHOOSE); + this.systemCursorConstants.set("screenshotCursor", pointer.PointerStyle.SCREENSHOT_CURSOR); + } + let pointStyle: pointer.PointerStyle = this.systemCursorConstants.get(kind); + if (pointStyle === null) { + return pointer.PointerStyle.DEFAULT; + } + return pointStyle; + } + + /** + * Detaches the text input plugin from the platform views controller; + * + *

The MouseCursorPlugin instance should not be used after call this. + */ + destroy(): void { + this.mouseCursorChannel.setMethodHandler(null); + } +} + diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/CustomTouchEvent.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/CustomTouchEvent.ets new file mode 100644 index 0000000000000000000000000000000000000000..aad347b421204abf744b51dbf6281c2348ebd5ec --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/CustomTouchEvent.ets @@ -0,0 +1,103 @@ +/* +* Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +export class CustomTouchEvent implements TouchEvent { + type: TouchType = 0; + touches: CustomTouchObject[]; + changedTouches: CustomTouchObject[]; + stopPropagation: () => void = () => { + }; + timestamp: number; + source: SourceType; + pressure: number; + tiltX: number; + tiltY: number; + sourceTool: SourceTool; + + constructor(type: TouchType, touches: CustomTouchObject[], changedTouches: CustomTouchObject[], timestamp: number, + source: SourceType, pressure: number, tiltX: number, tiltY: number, sourceTool: SourceTool) { + this.type = type; + this.touches = touches; + this.changedTouches = changedTouches; + this.timestamp = timestamp; + this.source = source; + this.pressure = pressure; + this.tiltX = tiltX; + this.tiltY = tiltY; + this.sourceTool = sourceTool; + } + + preventDefault: () => void = () => { + }; + + getModifierKeyState(keys: string[]): boolean { + throw new Error('Method not implemented.'); + } + + target: EventTarget = new CustomEventTarget(new CustomArea(0, 0, { x: 0, y: 0 }, { x: 0, y: 0 })); + + getHistoricalPoints(): HistoricalPoint[] { + throw new Error('Method not implemented.'); + } +} + +class CustomEventTarget implements EventTarget { + area: Area = new CustomArea(0, 0, { x: 0, y: 0 }, { x: 0, y: 0 }); + + constructor(area: Area) { + this.area = area; + } +} + +class CustomArea implements Area { + width: Length = 0; + height: Length = 0; + position: Position = { x: 0, y: 0 }; + globalPosition: Position = { x: 0, y: 0 }; + + constructor(width: Length, height: Length, position: Position, globalPosition: Position) { + this.width = width; + this.height = height; + this.position = position; + this.globalPosition = globalPosition; + } +} + +export class CustomTouchObject implements TouchObject { + type: TouchType; + id: number; + displayX: number; + displayY: number; + windowX: number; + windowY: number; + screenX: number; + screenY: number; + x: number; + y: number; + + constructor(type: TouchType, id: number, displayX: number, displayY: number, windowX: number, windowY: number, + screenX: number, screenY: number, x: number, y: number) { + this.type = type; + this.id = id; + this.displayX = displayX; + this.displayY = displayY; + this.windowX = windowX; + this.windowY = windowY; + this.screenX = screenX; + this.screenY = screenY; + this.x = x; + this.y = y; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformView.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformView.ets new file mode 100644 index 0000000000000000000000000000000000000000..9f480b97e767e05fe8d6e0e3bd172886227d6774 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformView.ets @@ -0,0 +1,94 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { DVModel, DynamicView } from '../../view/DynamicView/dynamicView' + +export declare class Params { + direction: Direction + platformView: PlatformView +} + +/** A handle to an DynamicView to be embedded in the Flutter hierarchy. */ +export default abstract class PlatformView { + + getType(): string { + return 'default'; + } + + /** Returns the DynamicView to be embedded in the Flutter hierarchy. */ + abstract getView(): WrappedBuilder<[Params]>; + + /** + * Called by the {@link io.flutter.embedding.engine.FlutterEngine} that owns this {@code + * PlatformView} when the DynamicView responsible for rendering a Flutter UI is + * associated with the {@link io.flutter.embedding.engine.FlutterEngine}. + * + *

This means that our associated {@link io.flutter.embedding.engine.FlutterEngine} can now + * render a UI and interact with the user. + * + *

Some platform views may have unusual dependencies on the {@link View} that renders Flutter + * UIs, such as unique keyboard interactions. That {@link View} is provided here for those + * purposes. Use of this {@link View} should be avoided if it is not absolutely necessary, because + * depending on this {@link View} will tend to make platform view code more brittle to future + * changes. + */ + onFlutterViewAttached(dvModel: DVModel): void { + } + + /** + * Called by the {@link io.flutter.embedding.engine.FlutterEngine} that owns this {@code + * PlatformView} when the DynamicView responsible for rendering a Flutter UI is detached + * and disassociated from the {@link io.flutter.embedding.engine.FlutterEngine}. + * + *

This means that our associated {@link io.flutter.embedding.engine.FlutterEngine} no longer + * has a rendering surface, or a user interaction surface of any kind. + * + *

This platform view must release any references related to the DynamicView that was + * provided in {@link #onFlutterViewAttached(View)}. + */ + onFlutterViewDetached(): void { + } + + /** + * Dispose this platform view. + * + *

The {@link PlatformView} object is unusable after this method is called. + * + *

Plugins implementing {@link PlatformView} must clear all references to the View object and + * the PlatformView after this method is called. Failing to do so will result in a memory leak. + * + *

References related to the DynamicView attached in {@link + * #onFlutterViewAttached(View)} must be released in {@code dispose()} to avoid memory leaks. + */ + abstract dispose(): void; + + /** + * Callback fired when the platform's input connection is locked, or should be used. + * + *

This hook only exists for rare cases where the plugin relies on the state of the input + * connection. This probably doesn't need to be implemented. + */ + onInputConnectionLocked(): void { + } + + /** + * Callback fired when the platform input connection has been unlocked. + * + *

This hook only exists for rare cases where the plugin relies on the state of the input + * connection. This probably doesn't need to be implemented. + */ + onInputConnectionUnlocked(): void { + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewFactory.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewFactory.ets new file mode 100644 index 0000000000000000000000000000000000000000..81ce2ba940d0decbebe39f9db33be4c5f2ec499d --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewFactory.ets @@ -0,0 +1,45 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import MessageCodec from '../common/MessageCodec'; +import PlatformView from './PlatformView' +import common from '@ohos.app.ability.common'; +import Any from '../common/Any'; + +export default abstract class PlatformViewFactory { + private createArgsCodec: MessageCodec; + + /** @param createArgsCodec the codec used to decode the args parameter of {@link #create}. */ + constructor(createArgsCodec: MessageCodec) { + this.createArgsCodec = createArgsCodec; + } + + /** + * Creates a new Dynamic be embedded in the Flutter hierarchy. + * + * @param context the context to be used when creating the view, this is different than + * FlutterView's context. + * @param viewId unique identifier for the created instance, this value is known on the Dart side. + * @param args arguments sent from the Flutter app. The bytes for this value are decoded using the + * createArgsCodec argument passed to the constructor. This is null if createArgsCodec was + * null, or no arguments were sent from the Flutter app. + */ + public abstract create(context: common.Context, viewId: number, args: Any): PlatformView; + + /** Returns the codec to be used for decoding the args parameter of {@link #create}. */ + getCreateArgsCodec(): MessageCodec { + return this.createArgsCodec; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewRegistry.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewRegistry.ets new file mode 100644 index 0000000000000000000000000000000000000000..2c49f5eeaddb54cece120df8eea307c69ad76ebf --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewRegistry.ets @@ -0,0 +1,32 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import PlatformViewFactory from './PlatformViewFactory' + +/** + * Registry for platform view factories. + * + *

Plugins can register factories for specific view types. + */ +export default interface PlatformViewRegistry { + /** + * Registers a factory for a platform view. + * + * @param viewTypeId unique identifier for the platform view's type. + * @param factory factory for creating platform views of the specified type. + * @return true if succeeded, false if a factory is already registered for viewTypeId. + */ + registerViewFactory(viewTypeId: string, factory: PlatformViewFactory): boolean; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewRegistryImpl.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewRegistryImpl.ets new file mode 100644 index 0000000000000000000000000000000000000000..98cb247d890cbe65150feb84242c01128047e7f9 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewRegistryImpl.ets @@ -0,0 +1,40 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import HashMap from '@ohos.util.HashMap'; +import PlatformViewFactory from './PlatformViewFactory' +import PlatformViewRegistry from './PlatformViewRegistry' + +export default class PlatformViewRegistryImpl implements PlatformViewRegistry { + // Maps a platform view type id to its factory. + private viewFactories: HashMap; + + constructor() { + this.viewFactories = new HashMap(); + } + + registerViewFactory(viewTypeId: string, factory: PlatformViewFactory): boolean { + if (this.viewFactories.hasKey(viewTypeId)) { + return false; + } + + this.viewFactories.set(viewTypeId, factory); + return true; + } + + getFactory(viewTypeId: string): PlatformViewFactory { + return this.viewFactories.get(viewTypeId); + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewWrapper.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewWrapper.ets new file mode 100644 index 0000000000000000000000000000000000000000..8f02bc27d24b92af3f4c080c97129ea72c47a617 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewWrapper.ets @@ -0,0 +1,87 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import OhosTouchProcessor from '../../embedding/ohos/OhosTouchProcessor'; +import { DVModel, DVModelParameters } from '../../view/DynamicView/dynamicView'; +import { createDVModelFromJson } from '../../view/DynamicView/dynamicViewJson'; +import { RootDvModeManager } from './RootDvModelManager'; +import matrix4 from '@ohos.matrix4' +import Log from '../../util/Log'; +import Any from '../common/Any'; + +const TAG: string = "PlatformViewWrapper"; + +export class PlatformViewWrapper { + private prevLeft: number = 0; + private prevTop: number = 0; + private left: number = 0; + private top: number = 0; + private bufferWidth: number = 0; + private bufferHeight: number = 0; + private touchProcessor: OhosTouchProcessor | null = null; + private model: DVModel | undefined; + + public setTouchProcessor(newTouchProcessor: OhosTouchProcessor): void { + this.touchProcessor = newTouchProcessor; + } + + constructor() { + } + + public getDvModel(): DVModel { + return this.model!; + } + + setParams: (params: DVModelParameters, key: string, element: Any) => void = + (params: DVModelParameters, key: string, element: Any): void => { + let params2 = params as Record; + params2[key] = element; + } + getParams: (params: DVModelParameters, element: string) => string | Any = + (params: DVModelParameters, element: string): string | Any => { + let params2 = params as Record; + return params2[element]; + } + + public setLayoutParams(parameters: DVModelParameters): void { + if (!this.model) { + return; + } + if (this.model.params == null) { + this.model.params = new DVModelParameters(); + } + this.setParams(this.model.params, "marginLeft", this.getParams(parameters, "marginLeft")); + this.setParams(this.model.params, "marginTop", this.getParams(parameters, "marginTop")); + this.left = this.getParams(parameters, "marginLeft"); + this.top = this.getParams(parameters, "marginTop"); + + this.setParams(this.model.params, "width", this.getParams(parameters, "width")); + this.setParams(this.model.params, "height", this.getParams(parameters, "height")); + } + + public addDvModel(model: DVModel): void { + this.model = model + } +} + +class DVModelParam { + compType: string + children: [] + + constructor(compType: string, children: []) { + this.compType = compType; + this.children = children; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewsController.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewsController.ets new file mode 100644 index 0000000000000000000000000000000000000000..1ee48b49852252c4ec301a07a3142cb32adb0ed9 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewsController.ets @@ -0,0 +1,497 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import PlatformViewsChannel, { + PlatformViewBufferResized, + PlatformViewCreationRequest, + PlatformViewResizeRequest, + PlatformViewsHandler, + PlatformViewTouch, + PlatformViewBufferSize +} from '../../../ets/embedding/engine/systemchannels/PlatformViewsChannel'; +import PlatformView, { Params } from './PlatformView'; +import { DVModelParameters, } from '../../view/DynamicView/dynamicView'; +import { createDVModelFromJson } from '../../view/DynamicView/dynamicViewJson'; +import display from '@ohos.display'; +import { FlutterView } from '../../view/FlutterView'; +import { TextureRegistry } from '../../view/TextureRegistry'; +import TextInputPlugin from '../editing/TextInputPlugin'; +import { PlatformViewWrapper } from './PlatformViewWrapper'; +import { FlutterOverlaySurface } from '../../embedding/engine/FlutterOverlaySurface'; +import HashSet from '@ohos.util.HashSet'; +import PlatformViewRegistry from './PlatformViewRegistry'; +import PlatformViewRegistryImpl from './PlatformViewRegistryImpl'; +import DartExecutor from '../../embedding/engine/dart/DartExecutor'; +import { FlutterMutatorView } from '../../embedding/engine/mutatorsstack/FlutterMutatorView'; +import Log from '../../util/Log' +import PlatformViewFactory from './PlatformViewFactory' +import { ByteBuffer } from '../../util/ByteBuffer'; +import Any from '../common/Any'; +import { ArrayList, Stack } from '@kit.ArkTS'; +import { CustomTouchEvent, CustomTouchObject } from './CustomTouchEvent'; +import { NodeRenderType } from '@kit.ArkUI'; +import { PlatformViewInfo } from '../../embedding/ohos/PlatformViewInfo'; +import { EmbeddingNodeController } from '../../embedding/ohos/EmbeddingNodeController'; + +class DVModelJson { + compType: string + children: Array + attributes: Any + events: Any + build: Any + + constructor(compType: string, children: Array, attributes: Any, events: Any, build?: Any) { + this.compType = compType + this.children = children + this.attributes = attributes + this.events = events; + this.build = build; + } +} + +const TAG = "PlatformViewsController" + +export default class PlatformViewsController implements PlatformViewsHandler { + private registry: PlatformViewRegistryImpl; + private context: Context | null = null; + private flutterView: FlutterView | null = null; + private textureRegistry: TextureRegistry | null = null; + private textInputPlugin: TextInputPlugin | null = null; + private platformViewsChannel: PlatformViewsChannel | null = null; + private nextOverlayLayerId: number = 0; + private platformViews: Map; + private viewIdWithTextureId: Map; + private viewIdWithNodeController: Map; + private viewWrappers: Map; + private currentFrameUsedOverlayLayerIds: HashSet; + private currentFrameUsedPlatformViewIds: HashSet; + private platformViewParent: Map; + + constructor() { + this.registry = new PlatformViewRegistryImpl(); + this.currentFrameUsedOverlayLayerIds = new HashSet(); + this.currentFrameUsedPlatformViewIds = new HashSet(); + this.viewWrappers = new Map(); + this.platformViews = new Map(); + this.viewIdWithTextureId = new Map(); + this.viewIdWithNodeController = new Map(); + this.platformViewParent = new Map(); + } + + createForPlatformViewLayer(request: PlatformViewCreationRequest): void { + Log.i(TAG, "Enter createForPlatformViewLayer"); + this.ensureValidRequest(request); + + let platformView: PlatformView = this.createPlatformView(request); + + this.configureForHybridComposition(platformView, request); + } + + dispose(viewId: number): void { + let platformView: PlatformView | null = this.platformViews.get(viewId) || null; + if (platformView == null) { + Log.e(TAG, "Disposing unknown platform view with id: " + viewId); + return; + } + this.platformViews.delete(viewId); + let textureId = this.viewIdWithTextureId.get(viewId); + + if (textureId != undefined) { + this.textureRegistry!.unregisterTexture(textureId); + } + + this.viewIdWithNodeController.get(viewId)?.disposeFrameNode() + this.viewIdWithNodeController.delete(viewId); + + let viewWrapper: PlatformViewWrapper | null = this.viewWrappers.get(viewId) || null; + if (viewWrapper != null && this.flutterView) { + let index = this.flutterView.getDVModel().children.indexOf(viewWrapper.getDvModel()!); + if (index > -1) { + this.flutterView.getDVModel().children.splice(index, 1); + } + } + this.viewWrappers.delete(viewId); + + try { + platformView.dispose(); + } catch (err) { + Log.e(TAG, "Disposing platform view threw an exception", err); + } + } + + setParams: (params: DVModelParameters, key: string, element: Any) => void = + (params: DVModelParameters, key: string, element: Any): void => { + let params2 = params as Record; + params2[key] = element; + } + + getParams: (params: DVModelParameters, key: string) => number = (params: DVModelParameters, key: string): number => { + let params2 = params as Record; + return params2[key]; + } + + resize(request: PlatformViewResizeRequest, onComplete: PlatformViewBufferResized): void { + let physicalWidth: number = this.toPhysicalPixels(request.newLogicalWidth); + let physicalHeight: number = this.toPhysicalPixels(request.newLogicalHeight); + let viewId: number = request.viewId; + Log.i(TAG, + `Resize viewId ${viewId}, pw:${physicalWidth}, ph:${physicalHeight},lw:${request.newLogicalWidth}, lh:${request.newLogicalHeight}`); + + let viewWrapper = this.viewWrappers.get(request.viewId) + let params: DVModelParameters | undefined = viewWrapper?.getDvModel()!.params + + this.setParams(params!, "width", physicalWidth); + this.setParams(params!, "height", physicalHeight); + + let textureId = this.viewIdWithTextureId.get(viewId); + if (textureId != undefined) { + let density = this.getDisplayDensity(); + this.textureRegistry?.notifyTextureResizing(textureId, request.newLogicalWidth * density, request.newLogicalHeight * density); + } + + onComplete.run(new PlatformViewBufferSize(physicalWidth, physicalHeight)); + } + + offset(viewId: number, top: number, left: number): void { + Log.i(TAG, `Offset is id${viewId}, t:${top}, l:${left}`); + + let viewWrapper = this.viewWrappers.get(viewId) + if (viewWrapper != undefined) { + let params: DVModelParameters | undefined = viewWrapper?.getDvModel()!.params + this.setParams(params!, "left", left); + this.setParams(params!, "top", top); + } + } + + onTouch(touch: PlatformViewTouch): void { + let viewWrapper: undefined | PlatformViewWrapper = this.viewWrappers.get(touch.viewId) + if (viewWrapper != undefined) { + let dvModel = viewWrapper.getDvModel() + let params = dvModel.getLayoutParams() as Record; + //接收到点击类型为down的时候 + if (touch.action == 0) { + //将当前点击状态设置为true + params['down'] = true + //首次收到触控点击类型为 OH_NATIVEXCOMPONENT_DOWN ,则将存到列表中的事件分发出去 + let touchEventArray: Array | undefined = params['touchEvent'] as Array + if (touchEventArray != undefined) { + let nodeController = params['nodeController'] as EmbeddingNodeController; + for (let it of touchEventArray) { + nodeController.postEvent(it) + } + //首次执行完之后,将列表数据置空 + params['touchEvent'] = undefined + } + //当前接收的事件类型为up的时候 + } else if (touch.action == 1) { + //手指抬起之后,将当前点击状态设置为false。测试了一下,多个手指突然抬起,最后返回的状态也是1 + //所以,这边就以状态抬起,代表当前用户不点击platformview了 + params['down'] = false + } + } + } + + setDirection(viewId: number, direction: Direction): void { + let nodeController = this.viewIdWithNodeController.get(viewId) + if (nodeController != undefined) { + nodeController?.setRenderOption(this.flutterView!.getPlatformView()!, this.flutterView!.getSurfaceId(), + NodeRenderType.RENDER_TYPE_TEXTURE, direction) + nodeController?.rebuild() + } + } + + validateDirection(direction: number): boolean { + return direction == Direction.Ltr || direction == Direction.Rtl || direction == Direction.Auto; + } + + clearFocus(viewId: number): void { + const platformView = this.platformViews.get(viewId); + if (platformView == null) { + Log.e(TAG, "Setting direction to an unknown view with id: " + viewId); + return; + } + const embeddedView = platformView.getView(); + if (embeddedView == null) { + Log.e(TAG, "Setting direction to a null view with id: " + viewId); + return; + } + // Make the Xcomponent gain focus. + focusControl.requestFocus(this.flutterView?.getId()); + } + + synchronizeToNativeViewHierarchy(yes: boolean): void { + throw new Error('Method not implemented.'); + } + + public createForTextureLayer(request: PlatformViewCreationRequest): number { + Log.i(TAG, "Enter createForTextureLayer"); + this.ensureValidRequest(request); + + let platformView: PlatformView = this.createPlatformView(request); + let textureId = this.configureForTextureLayerComposition(platformView, request); + this.viewIdWithTextureId.set(request.viewId, textureId); + return textureId; + } + + private ensureValidRequest(request: PlatformViewCreationRequest): void { + if (!this.validateDirection(request.direction)) { + throw new Error("Trying to create a view with unknown direction value: " + + request.direction + + "(view id: " + + request.viewId + + ")") + } + } + + private createPlatformView(request: PlatformViewCreationRequest): PlatformView { + Log.i(TAG, "begin createPlatformView"); + const viewFactory: PlatformViewFactory = this.registry.getFactory(request.viewType); + if (viewFactory == null) { + throw new Error("Trying to create a platform view of unregistered type: " + request.viewType) + } + + let createParams: Any = null; + if (request.params != null) { + let byteParas: ByteBuffer = request.params as ByteBuffer; + createParams = viewFactory.getCreateArgsCodec().decodeMessage(byteParas.buffer); + } + + if (this.context == null) { + throw new Error('PlatformView#context is null.'); + } + let platformView = viewFactory.create(this.context, request.viewId, createParams); + + let embeddedView: WrappedBuilder<[Params]> = platformView.getView(); + if (embeddedView == null) { + throw new Error("PlatformView#getView() returned null, but an WrappedBuilder reference was expected."); + } + + this.platformViews.set(request.viewId, platformView); + return platformView; + } + + // Configures the view for Hybrid Composition mode. + private configureForHybridComposition(platformView: PlatformView, request: PlatformViewCreationRequest): void { + Log.i(TAG, "Using hybrid composition for platform view: " + request.viewId); + } + + private configureForTextureLayerComposition(platformView: PlatformView, + request: PlatformViewCreationRequest): number { + Log.i(TAG, "Hosting view in view hierarchy for platform view: " + request.viewId); + let surfaceId: string = '0'; + let textureId: number = 0; + if (this.textureRegistry != null) { + textureId = this.textureRegistry!.getTextureId(); + surfaceId = this.textureRegistry!.registerTexture(textureId).getSurfaceId().toString(); + Log.i(TAG, "nodeController getSurfaceId: " + surfaceId); + this.flutterView!.setSurfaceId(surfaceId); + } + + let wrappedBuilder: WrappedBuilder<[Params]> = platformView.getView(); + this.flutterView?.setWrappedBuilder(wrappedBuilder); + this.flutterView?.setPlatformView(platformView); + let physicalWidth: number = this.toPhysicalPixels(request.logicalWidth); + let physicalHeight: number = this.toPhysicalPixels(request.logicalHeight); + + let nodeController = new EmbeddingNodeController(); + nodeController.setRenderOption(platformView, surfaceId, NodeRenderType.RENDER_TYPE_TEXTURE, request.direction); + this.viewIdWithNodeController.set(request.viewId, nodeController); + let dvModel = createDVModelFromJson(new DVModelJson("NodeContainer", + [], + { + "width": physicalWidth, + "height": physicalHeight, + "nodeController": nodeController, + "left": request.logicalLeft, + "top": request.logicalTop + }, + {}, + undefined)); + let viewWrapper: PlatformViewWrapper = new PlatformViewWrapper(); + viewWrapper.addDvModel(dvModel); + this.viewWrappers.set(request.viewId, viewWrapper); + this.flutterView?.getDVModel().children.push(viewWrapper.getDvModel()) + + Log.i(TAG, "Create platform view success"); + return textureId; + } + + public attach(context: Context, textureRegistry: TextureRegistry | null, dartExecutor: DartExecutor): void { + this.context = context; + this.textureRegistry = textureRegistry; + this.platformViewsChannel = new PlatformViewsChannel(dartExecutor); + this.platformViewsChannel.setPlatformViewsHandler(this); + } + + public detach(): void { + if (this.platformViewsChannel != null) { + this.platformViewsChannel.setPlatformViewsHandler(null); + } + this.destroyOverlaySurfaces(); + this.platformViewsChannel = null; + this.context = null; + this.textureRegistry = null; + } + + public attachToView(newFlutterView: FlutterView) { + this.flutterView = newFlutterView; + for (let wrapper of this.viewWrappers.values()) { + this.flutterView?.getDVModel().children.push(wrapper.getDvModel()!); + } + for (let mutator of this.platformViewParent.values()) { + this.flutterView?.getDVModel().children.push(mutator.getDvModel()!); + } + for (let platformView of this.platformViews.values()) { + platformView.onFlutterViewAttached(this.flutterView?.getDVModel()); + } + } + + public detachFromView(): void { + for (let index = 0; index < this.viewWrappers.size; index++) { + this.flutterView?.getDVModel().children.pop(); + } + for (let index = 0; index < this.platformViewParent.size; index++) { + this.flutterView?.getDVModel().children.pop(); + } + this.destroyOverlaySurfaces(); + this.removeOverlaySurfaces(); + this.flutterView = null; + + for (let platformView of this.platformViews.values()) { + platformView.onFlutterViewDetached(); + } + } + + public getFlutterView(): FlutterView | null { + return this.flutterView; + } + + public attachTextInputPlugin(textInputPlugin: TextInputPlugin): void { + this.textInputPlugin = textInputPlugin; + } + + public detachTextInputPlugin(): void { + this.textInputPlugin = null; + } + + public getRegistry(): PlatformViewRegistry { + return this.registry; + } + + public onDetachedFromNapi(): void { + this.diposeAllViews(); + } + + public onPreEngineRestart(): void { + this.diposeAllViews(); + } + + private getDisplayDensity(): number { + return display.getDefaultDisplaySync().densityPixels; + } + + private toPhysicalPixels(logicalPixels: number): number { + return Math.round(px2vp(logicalPixels * this.getDisplayDensity())); + } + + private toLogicalPixelsByDensity(physicalPixels: number, displayDensity: number): number { + return Math.round(physicalPixels / displayDensity); + } + + private toLogicalPixels(physicalPixels: number): number { + return this.toLogicalPixelsByDensity(physicalPixels, this.getDisplayDensity()); + } + + private diposeAllViews(): void { + let viewKeys = this.platformViews.keys(); + for (let viewId of viewKeys) { + this.dispose(viewId); + } + } + + private initializeRootImageViewIfNeeded(): void { + } + + public onDisplayOverlaySurface(id: number, x: number, y: number, width: number, height: number): void { + } + + public onBeginFrame(): void { + this.currentFrameUsedOverlayLayerIds.clear(); + this.currentFrameUsedPlatformViewIds.clear(); + } + + public onEndFrame(): void { + } + + private finishFrame(isFrameRenderedUsingImageReaders: boolean): void { + } + + public createOverlaySurface(): FlutterOverlaySurface { + return new FlutterOverlaySurface(this.nextOverlayLayerId++); + } + + private destroyOverlaySurfaces(): void { + } + + private removeOverlaySurfaces(): void { + if (!(this.flutterView instanceof FlutterView)) { + return; + } + } + + public render(surfaceId: number, platformView: PlatformView, + width: number, height: number, left: number, top: number) { + + let wrapper = this.viewWrappers.get(surfaceId); + if (wrapper != null) { + let params: DVModelParameters | undefined = wrapper?.getDvModel()!.params + + this.setParams(params!, "width", width); + this.setParams(params!, "height", height); + this.setParams(params!, "left", left); + this.setParams(params!, "top", top); + return; + } + + this.flutterView!.setSurfaceId(surfaceId.toString()); + let wrappedBuilder: WrappedBuilder<[Params]> = platformView.getView(); + this.flutterView?.setWrappedBuilder(wrappedBuilder); + this.flutterView?.setPlatformView(platformView); + + let nodeController = new EmbeddingNodeController(); + + nodeController.setRenderOption(platformView, surfaceId.toString(), NodeRenderType.RENDER_TYPE_TEXTURE, + Direction.Auto); + this.viewIdWithNodeController.set(surfaceId, nodeController); + + let dvModel = createDVModelFromJson(new DVModelJson("NodeContainer", + [], + { + "width": width, + "height": height, + "nodeController": nodeController, + "left": left, + "top": top + }, + {}, + undefined)); + + let viewWrapper: PlatformViewWrapper = new PlatformViewWrapper(); + viewWrapper.addDvModel(dvModel); + this.viewWrappers.set(surfaceId, viewWrapper); + this.flutterView?.getDVModel().children.push(viewWrapper.getDvModel()); + this.platformViews.set(surfaceId, platformView!); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/RawPointerCoord.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/RawPointerCoord.ets new file mode 100644 index 0000000000000000000000000000000000000000..f3dc6a29ab6cc5ed33b158d158ccf767c40b5d50 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/RawPointerCoord.ets @@ -0,0 +1,47 @@ +/* +* Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +export class RawPointerCoords { + private orientation: number = 0; + private pressure: number = 0; + private size: number = 0; + private toolMajor: number = 0; + private toolMinor: number = 0; + private touchMajor: number = 0; + private touchMinor: number = 0; + private x: number = 0; + private y: number = 0; + + constructor(orientation: number, pressure: number, size: number, toolMajor: number, toolMinor: number, + touchMajor: number, touchMinor: number, x: number, y: number) { + this.orientation = orientation; + this.pressure = pressure; + this.size = size; + this.toolMajor = toolMajor; + this.toolMinor = toolMinor; + this.touchMajor = touchMajor; + this.touchMinor = touchMinor; + this.x = x; + this.y = y; + } + + getX(): number { + return this.x; + } + + getY(): number { + return this.y; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/RootDvModelManager.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/RootDvModelManager.ets new file mode 100644 index 0000000000000000000000000000000000000000..2a8cfb4361b60fa79594a7dc1be0a0e5c726d1e9 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/RootDvModelManager.ets @@ -0,0 +1,39 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { + DVModel, + DVModelChildren, + DVModelContainer, + DVModelEvents, + DVModelParameters +} from '../../view/DynamicView/dynamicView'; +import Log from '../../util/Log'; + +export class RootDvModeManager { + private static model: DVModel = + new DVModel("Stack", new DVModelParameters(), new DVModelEvents(), new DVModelChildren(), null); + private static container: DVModelContainer = new DVModelContainer(RootDvModeManager.model); + + public static getRootDvMode(): DVModelContainer { + return RootDvModeManager.container; + } + + public static addDvModel(model: DVModel): void { + RootDvModeManager.container.model.children.push(model); + Log.i("flutter RootDvModeManager", 'DVModel: %{public}s', + JSON.stringify(RootDvModeManager.container.model.children) ?? ''); + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/ByteBuffer.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/ByteBuffer.ets new file mode 100644 index 0000000000000000000000000000000000000000..c349d77300771df27e55d3abd59daf07b6f6abf9 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/ByteBuffer.ets @@ -0,0 +1,815 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import util from '@ohos.util' +import StringUtils from './StringUtils' + +/** + * A byte buffer. + * + * Supports the following data types: + * - Bool + * - Int (8, 16, 32, 64) + * - Uint (8, 16, 32, 64) + * - BigInt (64) + * - String (utf8, utf16, and delimited) + * - TypedArray + * + */ +export class ByteBuffer { + /** + * Creates a byte buffer. + * @param source The data source. + * @param byteOffset The byte offset. + * @param byteLength The byte length. + * @returns A byte buffer. + */ + static from(source: ArrayBuffer, byteOffset?: number, byteLength?: number): ByteBuffer { + const byteBuffer = new ByteBuffer() + byteBuffer.dataView = byteLength === undefined ? new DataView(source, byteOffset) : + new DataView(source, byteOffset, Math.min(source.byteLength, byteLength)) + byteBuffer.mByteOffset = byteBuffer.dataView.byteOffset + return byteBuffer + } + + /** + * The dataView. + */ + private dataView?: DataView + /** + * The byte offset. + */ + mByteOffset: number = 0 + + /** + * The byte offset. + * @returns The byte offset. + */ + get byteOffset(): number { + return this.mByteOffset + } + + /** + * The byte offset. + * @returns The byte offset. + */ + get byteLength(): number { + return this.dataView?.byteLength ?? 0 + } + + /** + * The number of remaining bytes. + * @returns The number of bytes remaining. + */ + get bytesRemaining(): number { + return this.dataView ? this.dataView.byteLength - this.mByteOffset : 0; + } + + hasRemaining(): boolean { + return this.dataView != undefined && this.mByteOffset < this.dataView.byteLength; + } + + get buffer(): ArrayBuffer { + return this.dataView!.buffer.slice(0, this.mByteOffset) + } + + /** + * Skips the byte offset. + * @param byteLength The byte length. + */ + skip(byteLength: number): void { + this.mByteOffset += byteLength + } + + /** + * Resets the byte offset. + */ + reset(): void { + this.mByteOffset = this.dataView?.byteOffset ?? 0 + } + + /** + * Clears the byte buffer. + */ + clear(): void { + this.getUint8Array(0).fill(0) + } + + /** + * check buffer capacity. + */ + checkWriteCapacity(slen: number): void { + if (this.mByteOffset + slen > this.dataView!.byteLength) { + let newCapacity = this.dataView!.byteLength + (this.dataView!.byteLength >> 1); + if (newCapacity < this.dataView!.byteLength + slen + 512) { + newCapacity = this.dataView!.byteLength + slen + 512; + } + let newBuffer = new ArrayBuffer(newCapacity); + let newDataView = new DataView(newBuffer); + let oldUint8Array = new Uint8Array(this.dataView!.buffer); + let newUint8Array = new Uint8Array(newBuffer); + newUint8Array.set(oldUint8Array); + this.dataView = newDataView; + } + } + + /** + * Gets a boolean. + * @param byteOffset The byte offset. + */ + getBool(byteOffset: number): boolean { + return this.getInt8(byteOffset) !== 0 + } + + /** + * Reads the next boolean. + */ + readBool(): boolean { + return this.getInt8(this.mByteOffset++) !== 0 + } + + /** + * Sets a boolean. + * @param byteOffset The byte offset. + * @param value The value. + */ + setBool(byteOffset: number, value: boolean): void { + this.dataView?.setInt8(byteOffset, value ? 1 : 0) + } + + /** + * Writes the next boolean. + * @param value The value. + */ + writeBool(value: boolean): void { + this.checkWriteCapacity(1) + this.setInt8(this.mByteOffset++, value ? 1 : 0) + } + + /** + * Gets an signed byte. + * @param byteOffset The byte offset. + * @returns The value. + */ + getInt8(byteOffset: number): number { + return this.dataView?.getInt8(byteOffset) || 0 + } + + /** + * Reads the next signed byte. + * @returns The value. + */ + readInt8(): number { + return this.getInt8(this.mByteOffset++) + } + + /** + * Sets a signed byte. + * @param byteOffset The byte offset. + * @param value The value. + */ + setInt8(byteOffset: number, value: number): void { + this.dataView?.setInt8(byteOffset, value) + } + + /** + * Writes the next signed byte. + * @param value The value. + */ + writeInt8(value: number): void { + this.checkWriteCapacity(1) + this.setInt8(this.mByteOffset++, value) + } + + /** + * Gets an unsigned byte. + * @param byteOffset The byte offset. + * @returns The value. + */ + getUint8(byteOffset: number): number { + return this.dataView?.getUint8(byteOffset) || 0 + } + + /** + * Reads the next unsigned byte. + * @returns The value. + */ + readUint8(): number { + return this.getUint8(this.mByteOffset++) + } + + /** + * Sets an unsigned byte. + * @param byteOffset The byte offset. + * @param value The value. + */ + setUint8(byteOffset: number, value: number): void { + this.dataView?.setUint8(byteOffset, value) + } + + /** + * Writes the next signed byte. + * @param value The value. + */ + writeUint8(value: number): void { + this.checkWriteCapacity(1) + this.setUint8(this.mByteOffset++, value) + } + + /** + * Gets an signed short. + * @param byteOffset The byte offset. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + getInt16(byteOffset: number, littleEndian?: boolean): number { + return this.dataView?.getInt16(byteOffset, littleEndian) || 0 + } + + /** + * Reads the next signed short. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + readInt16(littleEndian?: boolean): number { + const value = this.getInt16(this.mByteOffset, littleEndian) + this.mByteOffset += 2 + return value + } + + /** + * Sets a signed short. + * @param byteOffset The byte offset. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + setInt16(byteOffset: number, value: number, littleEndian?: boolean): void { + this.dataView?.setInt16(byteOffset, value, littleEndian) + } + + /** + * Writes the next signed short. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + writeInt16(value: number, littleEndian?: boolean): void { + this.checkWriteCapacity(2) + this.setInt16(this.mByteOffset, value, littleEndian) + this.mByteOffset += 2 + } + + /** + * Gets an unsigned short. + * @param byteOffset The byte offset. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + getUint16(byteOffset: number, littleEndian?: boolean): number { + return this.dataView?.getUint16(byteOffset, littleEndian) || 0 + } + + /** + * Reads the next unsigned short. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + readUint16(littleEndian?: boolean): number { + const value = this.getUint16(this.mByteOffset, littleEndian) + this.mByteOffset += 2 + return value + } + + /** + * Sets an unsigned short. + * @param byteOffset The byte offset. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + setUint16(byteOffset: number, value: number, littleEndian?: boolean): void { + this.dataView?.setUint16(byteOffset, value, littleEndian) + } + + /** + * Writes the next signed short. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + writeUint16(value: number, littleEndian?: boolean): void { + this.checkWriteCapacity(2) + this.setUint16(this.mByteOffset, value, littleEndian) + this.mByteOffset += 2 + } + + /** + * Gets an signed integer. + * @param byteOffset The byte offset. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + getInt32(byteOffset: number, littleEndian?: boolean): number { + return this.dataView?.getInt32(byteOffset, littleEndian) ?? 0 + } + + /** + * Reads the next signed integer. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + readInt32(littleEndian?: boolean): number { + const value = this.getInt32(this.mByteOffset, littleEndian) + this.mByteOffset += 4 + return value + } + + /** + * Sets a signed integer. + * @param byteOffset The byte offset. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + setInt32(byteOffset: number, value: number, littleEndian?: boolean): void { + this.dataView?.setInt32(byteOffset, value, littleEndian) + } + + /** + * Writes the next signed integer. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + writeInt32(value: number, littleEndian?: boolean): void { + this.checkWriteCapacity(4) + this.setInt32(this.mByteOffset, value, littleEndian) + this.mByteOffset += 4 + } + + /** + * Gets an unsigned integer. + * @param byteOffset The byte offset. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + getUint32(byteOffset: number, littleEndian?: boolean): number { + return this.dataView?.getUint32(byteOffset, littleEndian) ?? 0 + } + + /** + * Reads the next unsigned integer. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + readUint32(littleEndian?: boolean): number { + const value = this.getUint32(this.mByteOffset, littleEndian) + this.mByteOffset += 4 + return value + } + + /** + * Sets an unsigned integer. + * @param byteOffset The byte offset. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + setUint32(byteOffset: number, value: number, littleEndian?: boolean): void { + this.dataView?.setUint32(byteOffset, value, littleEndian) + } + + /** + * Writes the next signed integer. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + writeUint32(value: number, littleEndian?: boolean): void { + this.checkWriteCapacity(4) + this.setUint32(this.mByteOffset, value, littleEndian) + this.mByteOffset += 4 + } + + /** + * Gets a float. + * @param byteOffset The byte offset. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + getFloat32(byteOffset: number, littleEndian?: boolean): number { + return this.dataView?.getFloat32(byteOffset, littleEndian) ?? 0 + } + + /** + * Reads the next float. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + readFloat32(littleEndian?: boolean): number { + const value = this.getFloat32(this.mByteOffset, littleEndian) + this.mByteOffset += 4 + return value + } + + /** + * Sets a float. + * @param byteOffset The byte offset. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + setFloat32(byteOffset: number, value: number, littleEndian?: boolean): void { + this.dataView?.setFloat32(byteOffset, value, littleEndian) + } + + /** + * Writes the next float. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + writeFloat32(value: number, littleEndian?: boolean): void { + this.checkWriteCapacity(4) + this.setFloat32(this.mByteOffset, value, littleEndian) + this.mByteOffset += 4 + } + + /** + * Gets a double. + * @param byteOffset The byte offset. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + getFloat64(byteOffset: number, littleEndian?: boolean): number { + return this.dataView?.getFloat64(byteOffset, littleEndian) ?? 0 + } + + /** + * Reads the next double. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + readFloat64(littleEndian?: boolean): number { + const value = this.getFloat64(this.mByteOffset, littleEndian) + this.mByteOffset += 8 + return value + } + + /** + * Sets a double. + * @param byteOffset The byte offset. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + setFloat64(byteOffset: number, value: number, littleEndian?: boolean): void { + this.dataView?.setFloat64(byteOffset, value, littleEndian) + } + + /** + * Writes the next double. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + writeFloat64(value: number, littleEndian?: boolean): void { + this.checkWriteCapacity(8) + this.setFloat64(this.mByteOffset, value, littleEndian) + this.mByteOffset += 8 + } + + /** + * Gets an signed long. + * @param byteOffset The byte offset. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + getBigInt64(byteOffset: number, littleEndian?: boolean): bigint { + return this.dataView?.getBigInt64(byteOffset, littleEndian) ?? BigInt(0) + } + + /** + * Reads the next signed long. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + readBigInt64(littleEndian?: boolean): bigint { + const value = this.getBigInt64(this.mByteOffset, littleEndian) + this.mByteOffset += 8 + return value + } + + /** + * Sets a signed long. + * @param byteOffset The byte offset. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + setBigInt64(byteOffset: number, value: bigint, littleEndian?: boolean): void { + this.dataView?.setBigInt64(byteOffset, value, littleEndian) + } + + /** + * Writes the next signed long. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + writeBigInt64(value: bigint, littleEndian?: boolean): void { + this.checkWriteCapacity(8) + this.setBigInt64(this.mByteOffset, value, littleEndian) + this.mByteOffset += 8 + } + + /** + * Gets an unsigned long. + * @param byteOffset The byte offset. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + getBigUint64(byteOffset: number, littleEndian?: boolean): bigint { + return this.dataView?.getBigUint64(byteOffset, littleEndian) ?? BigInt(0) + } + + /** + * Reads the next unsigned long. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + readBigUint64(littleEndian?: boolean): bigint { + const value = this.getBigUint64(this.mByteOffset, littleEndian) + this.mByteOffset += 8 + return value + } + + /** + * Sets an unsigned long. + * @param byteOffset The byte offset. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + setBigUint64(byteOffset: number, value: bigint, littleEndian?: boolean): void { + this.dataView?.setBigUint64(byteOffset, value, littleEndian) + } + + /** + * Writes the next unsigned long. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + writeBigUint64(value: bigint, littleEndian?: boolean): void { + this.checkWriteCapacity(8) + this.setBigUint64(this.mByteOffset, value, littleEndian) + this.mByteOffset += 8 + } + + /** + * Gets an signed long. + * @param byteOffset The byte offset. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + getInt64(byteOffset: number, littleEndian?: boolean): bigint { + return this.getBigInt64(byteOffset, littleEndian) + } + + /** + * Reads the next signed long. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + readInt64(littleEndian?: boolean): bigint { + const value = this.getInt64(this.mByteOffset, littleEndian) + this.mByteOffset += 8 + return value + } + + /** + * Sets a signed long. + * @param byteOffset The byte offset. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + setInt64(byteOffset: number, value: number, littleEndian?: boolean): void { + this.setBigInt64(byteOffset, BigInt(value), littleEndian) + } + + /** + * Writes the next signed long. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + writeInt64(value: number, littleEndian?: boolean): void { + this.checkWriteCapacity(8) + this.setInt64(this.mByteOffset, value, littleEndian) + this.mByteOffset += 8 + } + + /** + * Gets an unsigned long. + * @param byteOffset The byte offset. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + getUint64(byteOffset: number, littleEndian?: boolean): number { + return Number(this.getBigUint64(byteOffset, littleEndian)) + } + + /** + * Reads the next unsigned long. + * @param littleEndian If the value is little endian. + * @returns The value. + */ + readUint64(littleEndian?: boolean): number { + const value = this.getUint64(this.mByteOffset, littleEndian) + this.mByteOffset += 8 + return value + } + + /** + * Sets an unsigned long. + * @param byteOffset The byte offset. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + setUint64(byteOffset: number, value: number, littleEndian?: boolean): void { + this.setBigUint64(byteOffset, BigInt(value), littleEndian) + } + + /** + * Writes the next signed long. + * @param value The value. + * @param littleEndian If the value is little endian. + */ + writeUint64(value: number, littleEndian?: boolean): void { + this.checkWriteCapacity(8) + this.setUint64(this.mByteOffset, value, littleEndian) + this.mByteOffset += 8 + } + + /** + * Gets an array of unsigned bytes. + * @param byteOffset The byte offset. + * @param byteLength The byte length. + * @returns The value. + */ + getUint8Array(byteOffset: number, byteLength?: number): Uint8Array { + return this.dataView == null ? + new Uint8Array(StringUtils.stringToArrayBuffer(""), byteOffset, byteLength) : + new Uint8Array(this.dataView?.buffer, this.dataView?.byteOffset + byteOffset, byteLength) + } + + /** + * Reads the next array of unsigned bytes. + * @param byteLength The byte length. + * @returns The value. + */ + readUint8Array(byteLength?: number): Uint8Array { + const value = this.getUint8Array(this.mByteOffset, byteLength) + this.mByteOffset += value.byteLength + return value + } + + /** + * Sets an array of unsigned bytes. + * @param byteOffset The byte offset. + * @param value The value. + */ + setUint8Array(byteOffset: number, value: Uint8Array): void { + const byteLength = value.byteLength + this.getUint8Array(byteOffset, byteLength).set(value) + } + + /** + * Writes the next array of unsigned bytes. + * @param value The value. + */ + writeUint8Array(value: Uint8Array): void { + this.checkWriteCapacity(value.byteLength) + this.setUint8Array(this.mByteOffset, value) + this.mByteOffset += value.byteLength + } + + /** + * Gets an array of unsigned shorts. + * @param byteOffset The byte offset. + * @param byteLength The byte length. + * @returns The value. + */ + getUint16Array(byteOffset: number, byteLength?: number): Uint16Array { + if (byteLength !== undefined) { + byteLength = Math.floor(byteLength / 2) + } + return this.dataView == null ? + new Uint16Array(StringUtils.stringToArrayBuffer(""), byteOffset, byteLength) : + new Uint16Array(this.dataView.buffer, this.dataView.byteOffset + byteOffset, byteLength) + } + + /** + * Reads the next array of unsigned shorts. + * @param byteLength The byte length. + * @returns The value. + */ + readUint16Array(byteLength?: number): Uint16Array { + const value = this.getUint16Array(this.mByteOffset, byteLength) + this.mByteOffset += value.byteLength + return value + } + + /** + * Sets an array of unsigned bytes. + * @param byteOffset The byte offset. + * @param value The value. + */ + setUint16Array(byteOffset: number, value: Uint16Array): void { + const byteLength = value.byteLength + this.getUint16Array(byteOffset, byteLength).set(value) + } + + /** + * Writes the next array of unsigned bytes. + * @param value The value. + */ + writeUint16Array(value: Uint16Array): void { + this.checkWriteCapacity(value.byteLength) + this.setUint16Array(this.mByteOffset, value) + this.mByteOffset += value.byteLength + } + + /** + * Gets a string. + * @param byteOffset The byte offset. + * @param byteLength The byte length. + * @param byteEncoding The byte encoding. + * @returns The value. + */ + getString(byteOffset: number, byteLength?: number, byteEncoding?: string): string { + const decoder = new util.TextDecoder(byteEncoding || "utf-8") + const encoded = this.getUint8Array(byteOffset, byteLength) + return decoder.decode(encoded) + } + + /** + * Reads the next string. + * @param byteLength The byte length. + * @param byteEncoding The byte encoding. + * @returns The value. + */ + readString(byteLength?: number, byteEncoding?: string): string { + const value = this.getString(this.mByteOffset, byteLength, byteEncoding) + if (byteLength === undefined) { + this.mByteOffset = this.dataView?.byteLength ?? 0 + } else { + this.mByteOffset += byteLength + } + return value + } + + /** + * Sets a string. + * @param byteOffset The byte offset. + * @param value The string. + * @param byteEncoding The byte encoding. + * @returns The byte length. + */ + setString(byteOffset: number, value: string, byteEncoding?: string, write?: boolean): number { + if (byteEncoding && byteEncoding !== "utf-8") { + throw new TypeError("String encoding '" + byteEncoding + "' is not supported") + } + const encoder = new util.TextEncoder() + const byteLength = Math.min(this.dataView!.byteLength - byteOffset, value.length * 4) + if (write) { + this.checkWriteCapacity(byteLength) + } + const destination = this.getUint8Array(byteOffset, byteLength) + const written = encoder.encodeInto(value, destination).written + return written || 0 + } + + /** + * Writes the next a string. + * @param value The string. + * @param byteEncoding The byte encoding. + */ + writeString(value: string, byteEncoding?: string): void { + const byteLength = this.setString(this.mByteOffset, value, byteEncoding, true) + this.mByteOffset += byteLength + } + + /** + * Formats to a string. + * @param format The string format. + * @returns The string. + */ + toString(format?: string): string { + return [...this.getUint8Array(0)].map((byte: number) => { + switch (format) { + case "hex": + return ("00" + byte.toString(16)).slice(-2) + default: + return byte.toString(10) + } + }).join(" ") + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/Log.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/Log.ets new file mode 100644 index 0000000000000000000000000000000000000000..a9c5f89ec91e5ab74a96527018b86bc304bc33ed --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/Log.ets @@ -0,0 +1,128 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import HiLog from '@ohos.hilog'; +import BuildProfile from '../../../../BuildProfile'; + +const DOMAIN: number = 0x00FF; +const TAG = "Flutter"; +const SYMBOL = " --> "; + +/** + * Basic log class + */ +export default class Log { + private static _logLevel = HiLog.LogLevel.WARN; + + /** + * Set log level. + * + * @param level Indecated the log level. + */ + public static setLogLevel(level: HiLog.LogLevel) { + Log._logLevel = level; + } + + /** + * Outputs debug-level logs. + * + * @param tag Identifies the log tag. + * @param format Indicates the log format string. + * @param args Indicates the log parameters. + * @since 7 + */ + static d(tag: string, format: string, ...args: Object[]) { + if (Log.isLoggable(HiLog.LogLevel.DEBUG)) { + HiLog.debug(DOMAIN, TAG, tag + SYMBOL + format, args); + } + } + + /** + * Outputs info-level logs. + * + * @param tag Identifies the log tag. + * @param format Indicates the log format string. + * @param args Indicates the log parameters. + * @since 7 + */ + static i(tag: string, format: string, ...args: Object[]) { + if (Log.isLoggable(HiLog.LogLevel.INFO)) { + HiLog.info(DOMAIN, TAG, tag + SYMBOL + format, args); + } + } + + /** + * Outputs warning-level logs. + * + * @param tag Identifies the log tag. + * @param format Indicates the log format string. + * @param args Indicates the log parameters. + * @since 7 + */ + static w(tag: string, format: string, ...args: Object[]) { + if (Log.isLoggable(HiLog.LogLevel.WARN)) { + HiLog.warn(DOMAIN, TAG, tag + SYMBOL + format, args); + } + } + + /** + * Outputs error-level logs. + * + * @param tag Identifies the log tag. + * @param format Indicates the log format string. + * @param args Indicates the log parameters. + * @since 7 + */ + static e(tag: string, format: string, ...args: Object[]) { + if (Log.isLoggable(HiLog.LogLevel.ERROR)) { + args.forEach((item: Object, index: number) => { + if (item instanceof Error) { + args[index] = item.message + item.stack; + } + format += "%{public}s"; + }) + HiLog.error(DOMAIN, TAG, tag + SYMBOL + format, args); + } + } + + /** + * Outputs fatal-level logs. + * + * @param tag Identifies the log tag. + * @param format Indicates the log format string. + * @param args Indicates the log parameters. + * @since 7 + */ + static f(tag: string, format: string, ...args: Object[]) { + if (Log.isLoggable(HiLog.LogLevel.FATAL)) { + HiLog.fatal(DOMAIN, TAG, tag + SYMBOL + format, args); + } + } + + /** + * Checks whether logs of the specified tag, and level can be printed. + * + * @param tag Identifies the log tag. + * @param level log level + * @since 7 + */ + private static isLoggable(level: HiLog.LogLevel): boolean { + let buildModeName: string = BuildProfile.BUILD_MODE_NAME.toLowerCase(); + if (buildModeName == 'release' || buildModeName == 'profile') { + return level >= Log._logLevel && HiLog.isLoggable(DOMAIN, TAG, level); + } + return HiLog.isLoggable(DOMAIN, TAG, level); + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/MessageChannelUtils.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/MessageChannelUtils.ets new file mode 100644 index 0000000000000000000000000000000000000000..df543756c7239211d2ddd23b6dd295e42215f711 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/MessageChannelUtils.ets @@ -0,0 +1,25 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import BasicMessageChannel from '../plugin/common/BasicMessageChannel'; +import { BinaryMessenger } from '../plugin/common/BinaryMessenger'; +import StringUtils from './StringUtils'; + +export default class MessageChannelUtils { + static resizeChannelBuffer(messenger: BinaryMessenger, channel: string, newSize: number) { + const dataStr = `resize\r${channel}\r${newSize}` + messenger.send(BasicMessageChannel.CHANNEL_BUFFERS_CHANNEL, StringUtils.stringToArrayBuffer(dataStr)); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/PathUtils.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/PathUtils.ets new file mode 100644 index 0000000000000000000000000000000000000000..f47bcb68b0755dba14200d8a44db899528119191 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/PathUtils.ets @@ -0,0 +1,46 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import common from '@ohos.app.ability.common'; +import fs from '@ohos.file.fs'; +import Log from './Log'; + +/** + * ohos路径获取工具 + */ +const TAG: string = "PathUtils"; + +export default class PathUtils { + static getFilesDir(context: common.Context): string { + return context.filesDir; + } + + static getCacheDirectory(context: common.Context): string { + return context.cacheDir; + } + + static getDataDirectory(context: common.Context): string | null { + const name = "flutter"; + const flutterDir = context.filesDir + "/" + name; + if (!fs.accessSync(flutterDir)) { + try { + fs.mkdirSync(flutterDir); + } catch (err) { + Log.e(TAG, "mkdirSync failed err:" + err); + return null; + } + } + return flutterDir; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/StringUtils.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/StringUtils.ets new file mode 100644 index 0000000000000000000000000000000000000000..8ccc0e86563c685ac2bf3fb9bde33f163cf4cf9b --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/StringUtils.ets @@ -0,0 +1,51 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import flutter from 'libflutter.so' + +/** + * 默认字符串工具 + */ +export default class StringUtils { + + static stringToArrayBuffer(str: string): ArrayBuffer { + if (str.length == 0) { + return new ArrayBuffer(0); + } + return flutter.nativeEncodeUtf8(str).buffer; + } + + static arrayBufferToString(buffer: ArrayBuffer): string { + if (buffer.byteLength <= 0) { + return ""; + } + return flutter.nativeDecodeUtf8(new Uint8Array(buffer)); + } + + static uint8ArrayToString(buffer: Uint8Array): string { + if (buffer.length <= 0) { + return ""; + } + return flutter.nativeDecodeUtf8(buffer); + } + + static isNotEmpty(str: string): boolean { + return str != null && str.length > 0; + } + + static isEmpty(str: string): boolean { + return (!str) || str.length == 0; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/ToolUtils.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/ToolUtils.ets new file mode 100644 index 0000000000000000000000000000000000000000..1d13a5c93cb22c37739bcf4f39cac620920ee6ca --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/ToolUtils.ets @@ -0,0 +1,25 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import Any from '../plugin/common/Any'; + +export default class ToolUtils { + static isObj(object: Object): boolean { + return object && typeof (object) == 'object'; + } + + static implementsInterface(obj: Any, method: string): boolean { + return Reflect.has(obj, method) && typeof obj[method] === 'function' + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/TraceSection.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/TraceSection.ets new file mode 100644 index 0000000000000000000000000000000000000000..e74cbcce5f63e638a9cbac1e9cd59c693ca2b7a6 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/util/TraceSection.ets @@ -0,0 +1,45 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import hiTraceMeter from '@ohos.hiTraceMeter' + +export class TraceSection { + + static taskId: number = 0; + + private static cropSectionName(sectionName: string): string { + return sectionName.length < 124 ? sectionName : sectionName.substring(0, 124) + "..."; + } + + /** + * Wraps Trace.beginSection to ensure that the line length stays below 127 code units. + * + * @param sectionName The string to display as the section name in the trace. + */ + public static begin(sectionName: string): number { + TraceSection.taskId++; + hiTraceMeter.startTrace(TraceSection.cropSectionName(sectionName), TraceSection.taskId); + return TraceSection.taskId; + } + + /** Wraps Trace.endSection. */ + public static end(sectionName: string): void { + hiTraceMeter.finishTrace(TraceSection.cropSectionName(sectionName), TraceSection.taskId); + } + + public static endWithId(sectionName: string, id: number): void { + hiTraceMeter.finishTrace(TraceSection.cropSectionName(sectionName), id); + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/DynamicView/dynamicView.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/DynamicView/dynamicView.ets new file mode 100644 index 0000000000000000000000000000000000000000..0efac7cbf569e21c3ea214d0ad4fca213ba2d5e5 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/DynamicView/dynamicView.ets @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import matrix4 from '@ohos.matrix4'; +import Any from '../../plugin/common/Any'; +import Log from '../../util/Log'; + +/** + * Dynamic View creation + * from a recursive data structure + * + * exported @Component: DynamicView + * exported view model classes: + * - DVModelContainer + * - DVModel + * - DVModelParameters + * - DVModelEvents + * - DVModelChildren + * + * The purpose of exporting the DVModel classes + * is to make them available to the converter from + * JD's XML format and the expression parser. These + * components are expected to generate and update the + * DVModel. + * + * An application written by JS should only import + * DynamicView, DVModelContainer to be used in their own ArkUI + * container view. + */ + +/** + * View Model classes + */ + +@Observed +export class DVModelParameters extends Object { + /* empty, just get any instance wrapped inside an ObservedObject + with the help of the decoration */ +} + +@Observed +export class DVModelEvents extends Object { + /* empty, just get any instance wrapped inside an ObservedObject + with the help of the decoration */ +} + +@Observed +export class DVModelChildren extends Array { + /* empty, just get any instance wrapped inside an ObservedObject + with the help of the decoration */ +} + +let nextId: number = 1; + +@Observed +export class DVModel { + id_: number; + compType: string; + params: DVModelParameters; + events: DVModelEvents; + children: DVModelChildren; + builder: Any; + + constructor(compType: string, params: DVModelParameters, events: DVModelEvents, children: DVModelChildren, + builder?: Any) { + this.id_ = nextId++; + this.compType = compType; + this.params = params ?? new DVModelParameters; + this.events = events; + this.children = children; + this.builder = builder; + } + + public getLayoutParams(): DVModelParameters { + return this.params; + } +} + +// includes the root DVModel objects. +export class DVModelContainer { + model: DVModel; + + constructor(model: DVModel) { + this.model = model; + } +} + +/** + DynamicView is the @Component that does all the work: + + The following 4 features are the key solution elements for dynamic View + construction and update: + + 1. The if statement decides which framework component to create. + We can not use a factory function here, because that would requite calling + a regular function inside build() or a @Builder function. + + 2. Take note of the @Builder for Row, Column containers: + These functions create DynamicView Views inside a DynamicView + view. This behaviour is why we talk about DynamicView as a 'recursive' View. + All @Builder functions are member functions of the DynamicView @Component to + retain access ('this.xyz') to its decorated state variables. + + 3. The @Extend functions execute attribute and event handler registration functions + for all attributes and events permissable on the framework component, irrespective + if DVModelParameters or DVModelEvents objects includes a value or not. If not + the attribute or event is set to 'undefined' by intention. This is required to unset + any previously set value. + + 4. The scope ('this') of any lambda registered as an event hander function, e.g. for onClick, + is the @Component, in which the DVModel object is initialized. This said, it is advised to initialize + the DVModel object in the @Component that is parent to outmost DynamicView. Thereby, + any event handler function is able to mutate decorated state variables of that @Component + + */ + +@Component +export struct DynamicView { + @ObjectLink model: DVModel; + @ObjectLink children: DVModelChildren; + @ObjectLink params: DVModelParameters; + @ObjectLink events: DVModelEvents; + @BuilderParam customBuilder?: ($$: BuilderParams) => void; + getParams: (params: DVModelParameters, element: string) => string | Any = + (params: DVModelParameters, element: string): string | Any => { + let params2 = params as Record; + return params2[element]; + } + getEvents: (events: DVModelEvents, element: string) => Any = (events: DVModelEvents, element: string): Any => { + let events2 = events as Record; + return events2[element]; + } + + /* + we use this @Styles member function to set all common attributes and event handlers + and set component specific attribute and event handler functions in the @Builder function + */ + @Styles + common_attrs() { + .width(this.getParams(this.params, "width")) + .height(this.getParams(this.params, "height")) + .backgroundColor(this.getParams(this.params, "backgroundColor")) + .onClick(this.getEvents(this.events, "onClick")) + .margin({ + left: this.getParams(this.params, "marginLeft"), + right: this.getParams(this.params, "marginRight"), + top: this.getParams(this.params, "marginTop"), + bottom: this.getParams(this.params, "marginBottom") + }) + .onTouch(this.getEvents(this.events, "onTouch")) + .onFocus(this.getEvents(this.events, "onFocus")) + .onBlur(this.getEvents(this.events, "onBlur")) + .translate({ + x: this.getParams(this.params, "translateX"), + y: this.getParams(this.params, "translateY"), + z: this.getParams(this.params, "translateZ") + }) + .transform(this.getParams(this.params, "matrix")) + .direction(this.getParams(this.params, "direction")) + } + + @Styles + clip_attrs() { + .clip(this.getParams(this.params, "rectWidth") ? new Rect({ + width: this.getParams(this.params, "rectWidth"), + height: this.getParams(this.params, "rectHeight"), + radius: this.getParams(this.params, "rectRadius") + }) : null) + .clip(this.getParams(this.params, "pathWidth") ? new Path({ + width: this.getParams(this.params, "pathWidth"), + height: this.getParams(this.params, "pathHeight"), + commands: this.getParams(this.params, "pathCommands") + }) : null) + } + + @Builder + buildChildren() { + ForEach(this.children, + (child: Any) => { + DynamicView({ + model: child as DVModel, + params: child.params, + events: child.events, + children: child.children, + customBuilder: child.builder + }) + }, + (child: Any) => `${child.id_}` + ) + } + + @Builder + buildRow() { + Row() { + this.buildChildren() + } + .common_attrs() + .clip_attrs() + } + + @Builder + buildColumn() { + Column() { + this.buildChildren() + } + .common_attrs() + .clip_attrs() + } + + @Builder + buildStack() { + Stack() { + this.buildChildren() + } + .common_attrs() + .clip_attrs() + .alignContent(this.getParams(this.params, "alignContent")) + } + + @Builder + buildText() { + Text(`${this.getParams(this.params, "value")}`) + .common_attrs() + .fontColor(this.getParams(this.params, "fontColor")) + } + + @Builder + buildImage() { + Image(this.getParams(this.params, "src")) + .common_attrs() + } + + // Button with label + @Builder + buildButton() { + Button(this.getParams(this.params, "value")) + .common_attrs() + } + + @Builder + buildNodeContainer() { + NodeContainer(this.getParams(this.params, "nodeController")) + .common_attrs() + .position({ + x: (this.params as Record)['left'] as number, + y: (this.params as Record)['top'] as number + }) + } + + @Builder + buildCustom() { + if (this.customBuilder) { + this.customBuilder(new BuilderParams(this.params)); + } + } + + build() { + if (this.model.compType == "Column") { + this.buildColumn() + } else if (this.model.compType == "Row") { + this.buildRow() + } else if (this.model.compType == "Stack") { + this.buildStack() + } else if (this.model.compType == "Text") { + this.buildText() + } else if (this.model.compType == "Image") { + this.buildImage() + } else if (this.model.compType == "Button") { + this.buildButton() + } else if (this.model.compType == "NodeContainer") { + this.buildNodeContainer() + } else { + this.buildCustom() + } + } +} + +export class BuilderParams { + params: DVModelParameters; + + constructor(params: DVModelParameters) { + this.params = params; + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/DynamicView/dynamicViewJson.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/DynamicView/dynamicViewJson.ets new file mode 100644 index 0000000000000000000000000000000000000000..bf904a3b7b832b44aa55bc4267aeeff5b3f159a1 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/DynamicView/dynamicViewJson.ets @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import Any from '../../plugin/common/Any'; + +import { DVModel, DVModelParameters, DVModelEvents, DVModelChildren } from "./dynamicView"; +import Log from '../../util/Log'; +const TAG = "dynamicViewJson"; + +export function createDVModelFromJson(json: Object): DVModel { + + /* private use helper functions */ + let createChildrenFrom: (children: Array) => DVModelChildren = (children: Array): DVModelChildren => { + let result = new DVModelChildren(); + if (Array.isArray(children)) { + (children as Array).forEach(child => { + const childView = createDVModelFromJson(child); + if (childView != undefined) { + result.push(childView); + } + }); + } + return result; + } + + let setParams: (result: DVModelParameters | DVModelEvents, key: Any, element: Object) => void = + (result: DVModelParameters, key: Any, element: Any): void => { + let newResult = result as Record; + newResult[key] = element[key]; + } + + let createAttributesFrom: (attributes: Object) => DVModelParameters = (attributes: Object): DVModelParameters => { + let result = new DVModelParameters(); + if ((typeof attributes == "object") && (!Array.isArray(attributes))) { + Object.keys(attributes).forEach(k => { + setParams(result, k, attributes) + }); + } + return result; + } + + let createEventsFrom: (events: Object) => DVModelEvents = (events: Object): DVModelEvents => { + let result = new DVModelEvents(); + if ((typeof events == "object") && (!Array.isArray(events))) { + Object.keys(events).forEach(k => { + setParams(result, k, events) + }); + } + return result; + } + + if (typeof json !== 'object') { + Log.e(TAG, "createDVModelFromJson: input is not JSON"); + return new DVModel("", "", "", createChildrenFrom([])); + } + + let jsonObject = json as Record; + return new DVModel( + jsonObject["compType"], + createAttributesFrom(jsonObject["attributes"]), + createEventsFrom(jsonObject["events"]), + createChildrenFrom(jsonObject["children"]), + jsonObject["build"] + ); +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterCallbackInformation.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterCallbackInformation.ets new file mode 100644 index 0000000000000000000000000000000000000000..7f9111e95f95d9784422a7695dd0db712690763e --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterCallbackInformation.ets @@ -0,0 +1,45 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import FlutterNapi from '../embedding/engine/FlutterNapi'; + +export class FlutterCallbackInformation { + callbackName?: string; + callbackClassName?: string; + callbackLibraryPath?: string; + + /** + * Get callback information for a given handle. + * + * @param handle the handle for the callback, generated by `PluginUtilities.getCallbackHandle` in + * `dart:ui`. + * @return an instance of FlutterCallbackInformation for the provided handle. + */ + static lookupCallbackInformation(handle: number): FlutterCallbackInformation | null { + return FlutterNapi.nativeLookupCallbackInformation(handle); + } + + constructor(callbackName?: string, callbackClassName?: string, callbackLibraryPath?: string) { + this.callbackName = callbackName; + this.callbackClassName = callbackClassName; + this.callbackLibraryPath = callbackLibraryPath; + } + + init(callbackName: string, callbackClassName: string, callbackLibraryPath: string) { + this.callbackName = callbackName; + this.callbackClassName = callbackClassName; + this.callbackLibraryPath = callbackLibraryPath; + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterRunArguments.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterRunArguments.ets new file mode 100644 index 0000000000000000000000000000000000000000..c81e33f381f06662a8b881df3452d76038837361 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterRunArguments.ets @@ -0,0 +1,26 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +export default class FlutterRunArguments { + public bundlePath: string; + public entrypoint: string; + public libraryPath: string; + + constructor(bundlePath: string, entrypoint: string, libraryPath: string) { + this.bundlePath = bundlePath; + this.entrypoint = entrypoint; + this.libraryPath = libraryPath; + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterView.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterView.ets new file mode 100644 index 0000000000000000000000000000000000000000..271a72ee5f618c3045320700c86b1b247cd1d70f --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterView.ets @@ -0,0 +1,702 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import FlutterEngine from '../embedding/engine/FlutterEngine'; +import Log from '../util/Log'; +import { DVModel, DVModelChildren, DVModelEvents, DVModelParameters } from './DynamicView/dynamicView'; +import { display } from '@kit.ArkUI' +import FlutterManager from '../embedding/ohos/FlutterManager'; +import window from '@ohos.window'; +import KeyboardManager from '../embedding/ohos/KeyboardManager'; +import MouseCursorPlugin from '../plugin/mouse/MouseCursorPlugin'; +import Settings from '../embedding/ohos/Settings'; +import ArrayList from '@ohos.util.ArrayList'; +import PlatformView, { Params } from '../plugin/platform/PlatformView'; +import hiTraceMeter from '@ohos.hiTraceMeter' +import { JSON } from '@kit.ArkTS'; +import TextInputPlugin from '../plugin/editing/TextInputPlugin'; +import { accessibility } from '@kit.AccessibilityKit'; + +const TAG = "FlutterViewTag"; + +export class ViewportMetrics { + devicePixelRatio: number = 1.0; + physicalWidth: number = 0; + physicalHeight: number = 0; + physicalViewPaddingTop: number = 0; + physicalViewPaddingRight: number = 0; + physicalViewPaddingBottom: number = 0; + physicalViewPaddingLeft: number = 0; + physicalViewInsetTop: number = 0; + physicalViewInsetRight: number = 0; + physicalViewInsetBottom: number = 0; + physicalViewInsetLeft: number = 0; + systemGestureInsetTop: number = 0; + systemGestureInsetRight: number = 0; + systemGestureInsetBottom: number = 0; + systemGestureInsetLeft: number = 0; + physicalTouchSlop: number = -1; + displayFeatures: ArrayList = new ArrayList(); + + clone(): ViewportMetrics { + const copy = new ViewportMetrics(); + copy.devicePixelRatio = this.devicePixelRatio; + copy.physicalWidth = this.physicalWidth; + copy.physicalHeight = this.physicalHeight; + copy.physicalViewPaddingTop = this.physicalViewPaddingTop; + copy.physicalViewPaddingRight = this.physicalViewPaddingRight; + copy.physicalViewPaddingBottom = this.physicalViewPaddingBottom; + copy.physicalViewPaddingLeft = this.physicalViewPaddingLeft; + copy.physicalViewInsetTop = this.physicalViewInsetTop; + copy.physicalViewInsetRight = this.physicalViewInsetRight; + copy.physicalViewInsetBottom = this.physicalViewInsetBottom; + copy.physicalViewInsetLeft = this.physicalViewInsetLeft; + copy.systemGestureInsetTop = this.systemGestureInsetTop; + copy.systemGestureInsetRight = this.systemGestureInsetRight; + copy.systemGestureInsetBottom = this.systemGestureInsetBottom; + copy.systemGestureInsetLeft = this.systemGestureInsetLeft; + copy.physicalTouchSlop = this.physicalTouchSlop; + copy.displayFeatures = this.displayFeatures; + return copy; + } + + isEqual(other: ViewportMetrics): boolean { + return this.devicePixelRatio === other.devicePixelRatio && + this.physicalWidth === other.physicalWidth && + this.physicalHeight === other.physicalHeight && + this.physicalViewPaddingTop === other.physicalViewPaddingTop && + this.physicalViewPaddingRight === other.physicalViewPaddingRight && + this.physicalViewPaddingBottom === other.physicalViewPaddingBottom && + this.physicalViewPaddingLeft === other.physicalViewPaddingLeft && + this.physicalViewInsetTop === other.physicalViewInsetTop && + this.physicalViewInsetRight === other.physicalViewInsetRight && + this.physicalViewInsetBottom === other.physicalViewInsetBottom && + this.physicalViewInsetLeft === other.physicalViewInsetLeft && + this.systemGestureInsetTop === other.systemGestureInsetTop && + this.systemGestureInsetRight === other.systemGestureInsetRight && + this.systemGestureInsetBottom === other.systemGestureInsetBottom && + this.systemGestureInsetLeft === other.systemGestureInsetLeft && + this.physicalTouchSlop === other.physicalTouchSlop && + this.displayFeatures === other.displayFeatures; + } +} + +export class DisplayFeature { + bounds: display.Rect[]; + type: DisplayFeatureType; + state: DisplayFeatureState; + + constructor(bounds: display.Rect[], type: DisplayFeatureType, state: DisplayFeatureState) { + this.bounds = bounds; + this.type = type; + this.state = state; + } + + getBounds(): display.Rect[] { + return this.bounds; + } + + getType(): DisplayFeatureType { + return this.type; + } + + getState(): DisplayFeatureState { + return this.state + } + + setBounds(bounds: display.Rect[]): void { + this.bounds = bounds; + } + + setType(type: DisplayFeatureType): void { + this.type = type; + } + + setState(state: DisplayFeatureState): void { + this.state = state; + } +} + +export enum DisplayFeatureType { + UNKNOWN = 0, + FOLD = 1, + HINGE = 2, + CUTOUT = 3 +} + +export enum DisplayFeatureState { + UNKNOWN = 0, + POSTURE_FLAT = 1, + POSTURE_HALF_OPENED = 2, +} + +export enum DisplayFoldStatus { + FOLD_STATUS_UNKNOWN = 0, + FOLD_STATUS_EXPANDED = 1, + FOLD_STATUS_FOLDED = 2, + FOLD_STATUS_HALF_FOLDED = 3 +} + +export class FlutterView { + private flutterEngine: FlutterEngine | null = null + private id: string = "" + private dVModel: DVModel = + new DVModel("Stack", new DVModelParameters(), new DVModelEvents(), new DVModelChildren(), null); + private wrapBuilder: WrappedBuilder<[Params]> | undefined = undefined; + private platformView: PlatformView | undefined = undefined; + private isSurfaceAvailableForRendering: boolean = false + private viewportMetrics = new ViewportMetrics(); + private displayInfo?: display.Display; + private keyboardManager: KeyboardManager | null = null; + private mainWindow: window.Window | null = null; + private mouseCursorPlugin?: MouseCursorPlugin; + private textInputPlugin?: TextInputPlugin; + private uiContext?: UIContext | undefined; + private settings?: Settings; + private mFirstFrameListeners: ArrayList; + private mFirstPreloadFrameListeners: ArrayList; + private isFlutterUiDisplayed: boolean = false; + private isFlutterUiPreload: boolean = false; + private surfaceId: string = "0"; + private checkFullScreen: boolean = true; + private checkKeyboard: boolean = true; + private checkGesture: boolean = true; + private checkAiBar: boolean = true; + private frameCache: boolean = true; + private systemAvoidArea: window.AvoidArea; + private navigationAvoidArea: window.AvoidArea; + private gestureAvoidArea: window.AvoidArea; + private keyboardAvoidArea: window.AvoidArea; + private needSetViewport: boolean = false; + private windowPosition: window.Rect | null = null; + + constructor(viewId: string, context: Context) { + this.id = viewId + this.displayInfo = display.getDefaultDisplaySync(); + this.viewportMetrics.devicePixelRatio = this.displayInfo?.densityPixels; + this.buildDisplayFeatures(display.getFoldStatus()); + this.viewportMetrics.physicalTouchSlop = 1.0 * this.displayInfo?.densityPixels; + + this.mainWindow = FlutterManager.getInstance() + .getWindowStage(FlutterManager.getInstance().getUIAbility(context)) + .getMainWindowSync(); + this.mFirstFrameListeners = new ArrayList(); + this.mFirstPreloadFrameListeners = new ArrayList(); + + this.mainWindow?.on('windowSizeChange', this.windowSizeChangeCallback); + this.mainWindow?.on('avoidAreaChange', this.avoidAreaChangeCallback); + this.mainWindow?.on('windowStatusChange', this.windowStatusChangeCallback); + this.mainWindow?.on('keyboardHeightChange', this.keyboardHeightChangeCallback); + this.mainWindow?.on('windowRectChange', this.windowRectChangeCallback); + //监听系统无障碍服务状态改变 + accessibility.on('accessibilityStateChange', this.accessibilityStateChangeCallback); + + this.systemAvoidArea = this.mainWindow?.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM); + this.navigationAvoidArea = this.mainWindow?.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR); + this.gestureAvoidArea = this.mainWindow?.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM_GESTURE); + this.keyboardAvoidArea = this.mainWindow?.getWindowAvoidArea(window.AvoidAreaType.TYPE_KEYBOARD); + + // 监听折叠状态的改变 + display?.on('foldStatusChange', this.foldStatusChangeCallback); + + // Subscribes to display changes. Example: event that the display size is changed. + try { + display.on("change", this.displayChangeCallback); + } catch (e) { + Log.e(TAG, "displayInfo error" + JSON.stringify(e)); + } + + } + + private async buildDisplayFeatures(foldStatus: display.FoldStatus) { + let displayFeatures: ArrayList = new ArrayList(); + let bound: display.Rect[]; + let state: DisplayFeatureState = DisplayFeatureState.UNKNOWN; + let type: DisplayFeatureType = DisplayFeatureType.FOLD; + const displayInfos = display.getDefaultDisplaySync(); + if (display.isFoldable()) { + switch (foldStatus) { + case display.FoldStatus.FOLD_STATUS_EXPANDED: + type = DisplayFeatureType.FOLD; + state = DisplayFeatureState.POSTURE_FLAT; + break; + case display.FoldStatus.FOLD_STATUS_HALF_FOLDED: + type = DisplayFeatureType.FOLD; + state = DisplayFeatureState.POSTURE_HALF_OPENED; + break; + case display.FoldStatus.FOLD_STATUS_UNKNOWN: + type = DisplayFeatureType.UNKNOWN; + state = DisplayFeatureState.UNKNOWN; + break; + default: + state = DisplayFeatureState.UNKNOWN; + type = DisplayFeatureType.FOLD; + break; + } + const displays = await display.getAllDisplays(); + for (let i = 0; i < displays.length; i++) { + let cutoutInfo = await displays[i].getCutoutInfo(); + bound = cutoutInfo.boundingRects; + displayFeatures.add(new DisplayFeature(bound, type, state)); + } + Log.d(TAG, `FOLD device displayFeatures is : ${JSON.stringify(displayFeatures)}`) + } else { + type = DisplayFeatureType.CUTOUT; + state = DisplayFeatureState.UNKNOWN; + let infos = await displayInfos?.getCutoutInfo(); + bound = infos.boundingRects; + displayFeatures.add(new DisplayFeature(bound, type, state)); + Log.d(TAG, `UNFold device displayFeatures is : ${JSON.stringify(displayFeatures)}`) + } + this.viewportMetrics.displayFeatures = displayFeatures; + this.updateViewportMetrics(); + } + + private avoidAreaChangeCallback = (data: window.AvoidAreaOptions) => { + Log.i(TAG, "avoidAreaChangeCallback type:" + data.type); + if (data.type == window.AvoidAreaType.TYPE_SYSTEM) { //0 + this.systemAvoidArea = data.area; + } else if (data.type == window.AvoidAreaType.TYPE_SYSTEM_GESTURE) { //2 + this.gestureAvoidArea = data.area; + } else if (data.type == window.AvoidAreaType.TYPE_KEYBOARD) { //3 + this.keyboardAvoidArea = data.area; + } else if (data.type == window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR) { //4 + this.navigationAvoidArea = data.area; + } + if (this.isAttachedToFlutterEngine()) { + this.onAreaChange(null); + } + } + private windowSizeChangeCallback = (data: window.Size) => { + Log.i(TAG, "windowSizeChangeCallback w:" + data.width + ", h:" + data.height); + if (this.isAttachedToFlutterEngine()) { + this.onAreaChange(null); + } + } + private windowStatusChangeCallback = (data: window.WindowStatusType) => { + Log.i(TAG, "windowStatusChangeCallback " + data); + if (this.isAttachedToFlutterEngine()) { + FlutterManager.getInstance().getFullScreenListener().onScreenStateChanged(data); + } + }; + private displayChangeCallback = (data: number) => { + this.displayInfo = display.getDefaultDisplaySync(); + this.flutterEngine?.getTextInputChannel()?.setDevicePixelRatio(this.displayInfo.densityPixels); + let devicePixelRatio: number = this.displayInfo?.densityPixels; + let physicalTouchSlop: number = 1.0 * this.displayInfo?.densityPixels; + Log.i(TAG, "Display on: " + JSON.stringify(this.displayInfo) + ". Display id:" + JSON.stringify(data)) + if (devicePixelRatio != this.viewportMetrics.devicePixelRatio || + this.viewportMetrics.physicalTouchSlop != physicalTouchSlop) { + this.viewportMetrics.devicePixelRatio = devicePixelRatio; + this.viewportMetrics.physicalTouchSlop = physicalTouchSlop + this.needSetViewport = true; + this.onAreaChange(null); + } + } + private keyboardHeightChangeCallback = (data: number) => { + Log.i(TAG, "keyboardHeightChangeCallback " + data); + this.keyboardAvoidArea.bottomRect.height = data; + this.onAreaChange(null); + }; + private windowRectChangeCallback = (data: window.RectChangeOptions) => { + Log.i(TAG, "windowRectChangeCallback " + data); + this.windowPosition = data.rect as window.Rect; + this.flutterEngine?.getTextInputChannel()?.setWindowPosition(this.windowPosition); + } + private accessibilityStateChangeCallback = (data: boolean) => { + Log.i(TAG, `subscribe accessibility state change, result: ${JSON.stringify(data)}`); + this.flutterEngine?.getFlutterNapi()?.accessibilityStateChange(data); + } + private foldStatusChangeCallback = (data: display.FoldStatus) => { + Log.d(TAG, `Fold status change to ${JSON.stringify(data)}`) + this.buildDisplayFeatures(data); + } + + getId(): string { + return this.id; + } + + setSurfaceId(surfaceId: string): void { + this.surfaceId = surfaceId; + } + + getSurfaceId(): string { + return this.surfaceId; + } + + setWrappedBuilder(wrappedBuilder: WrappedBuilder<[Params]>) { + this.wrapBuilder = wrappedBuilder; + } + + getWrappedBuilder(): WrappedBuilder<[Params]> | undefined { + return this.wrapBuilder; + } + + setPlatformView(platformView: PlatformView) { + this.platformView = platformView; + } + + getPlatformView(): PlatformView | undefined { + return this.platformView; + } + + getDVModel() { + return this.dVModel; + } + + getKeyboardHeight() { + return this.keyboardAvoidArea.bottomRect.height + } + + onDestroy() { + try { + this.mainWindow?.off('windowSizeChange', this.windowSizeChangeCallback); + this.mainWindow?.off('avoidAreaChange', this.avoidAreaChangeCallback); + this.mainWindow?.off('windowStatusChange', this.windowStatusChangeCallback); + this.mainWindow?.off('keyboardHeightChange', this.keyboardHeightChangeCallback); + this.mainWindow?.off('windowRectChange', this.windowRectChangeCallback); + accessibility.off('accessibilityStateChange', this.accessibilityStateChangeCallback); + display.off('foldStatusChange', this.foldStatusChangeCallback); + } catch (e) { + Log.e(TAG, "mainWindow off error: " + JSON.stringify(e)); + } + this.mainWindow = null; + + try { + display.off("change", this.displayChangeCallback); + } catch (e) { + Log.e(TAG, "displayInfo off error" + JSON.stringify(e)); + } + } + + attachToFlutterEngine(flutterEngine: FlutterEngine): void { + hiTraceMeter.startTrace("attachToFlutterEngine", 0); + if (this.isAttachedToFlutterEngine()) { + if (flutterEngine == this.flutterEngine) { + Log.i(TAG, "Already attached to this engine. Doing nothing."); + return; + } + // Detach from a previous FlutterEngine so we can attach to this new one.f + Log.i( + TAG, + "Currently attached to a different engine. Detaching and then attaching" + + " to new engine."); + this.detachFromFlutterEngine(); + } + Log.i(TAG, "attachToFlutterEngine"); + this.flutterEngine = flutterEngine; + this.flutterEngine?.getFlutterNapi().xComponentAttachFlutterEngine(this.id) + this.flutterEngine?.getFlutterNapi()?.updateRefreshRate(this.displayInfo!.refreshRate) + this.flutterEngine?.getFlutterNapi()?.updateSize(this.displayInfo!.width, this.displayInfo!.height) + this.flutterEngine?.getFlutterNapi()?.updateDensity(this.displayInfo!.densityPixels) + this.flutterEngine?.getFlutterNapi().enableFrameCache(this.frameCache); + if (accessibility.isOpenAccessibilitySync()) { + this.flutterEngine?.getFlutterNapi()?.accessibilityStateChange(true); + } + flutterEngine.getPlatformViewsController()?.attachToView(this); + + let newArea: Area | null = { + width: px2vp(this.displayInfo!.width), + height: px2vp(this.displayInfo!.height), + position: { x: 0, y: 0 }, + globalPosition: { x: 0, y: 0 } + }; + if (this.viewportMetrics.physicalWidth != 0 || this.viewportMetrics.physicalHeight != 0) { + newArea = null; + } + this.onAreaChange(newArea, true); + + let windowId = this.mainWindow?.getWindowProperties()?.id ?? 0 + this.mouseCursorPlugin = new MouseCursorPlugin(windowId, this.flutterEngine?.getMouseCursorChannel()!); + this.textInputPlugin = new TextInputPlugin(this.flutterEngine?.getTextInputChannel()!, this.id); + this.keyboardManager = new KeyboardManager(flutterEngine, this.textInputPlugin!); + this.settings = new Settings(this.flutterEngine.getSettingsChannel()!); + this.sendSettings(); + this.isFlutterUiDisplayed = this.flutterEngine.getFlutterNapi().isDisplayingFlutterUi; + this.isFlutterUiPreload = this.flutterEngine.getFlutterNapi().isPreloadedFlutterUi; + if (this.isFlutterUiPreload) { + this.onFirstFrame(1); + } + if (this.isFlutterUiDisplayed) { + this.onFirstFrame(); + } + if (this.isSurfaceAvailableForRendering) { + this.flutterEngine?.processPendingMessages(); + } + hiTraceMeter.finishTrace("attachToFlutterEngine", 0); + } + + preDraw(width: number = 0, height: number = 0): void { + if (this.isAttachedToFlutterEngine()) { + if (width == 0 || height == 0) { + width = this.displayInfo!.width; + height = this.displayInfo!.height; + } + this.flutterEngine?.getFlutterNapi().xComponentPreDraw(this.id, width, height); + } + } + + detachFromFlutterEngine(): void { + Log.i(TAG, "detachFromFlutterEngine"); + if (!this.isAttachedToFlutterEngine()) { + Log.d(TAG, "FlutterView not attached to an engine. Not detaching."); + return; + } + if (this.isSurfaceAvailableForRendering) { + this.flutterEngine!!.getFlutterNapi().xComponentDetachFlutterEngine(this.id) + } + this.flutterEngine?.getPlatformViewsController()?.detachFromView(); + this.flutterEngine = null; + this.keyboardManager = null; + this.textInputPlugin?.destroy(); + } + + onWindowCreated() { + Log.d(TAG, "received onwindowCreated."); + let _UIContext = this.mainWindow?.getUIContext(); + this.uiContext = _UIContext; + this.sendSettings(); + Log.d(TAG, "uiContext init and sendSettings finished."); + } + + sendSettings(): void { + if (this.uiContext != undefined && this.isAttachedToFlutterEngine()) { + this.settings?.sendSettings(this.uiContext.getMediaQuery()); + } else { + Log.e(TAG, "UIContext is null, cannot send Settings!"); + } + } + + onSurfaceCreated() { + this.isSurfaceAvailableForRendering = true; + this.flutterEngine?.processPendingMessages(); + } + + onSurfaceDestroyed() { + this.isSurfaceAvailableForRendering = false; + if (this.isAttachedToFlutterEngine()) { + this.flutterEngine!!.getFlutterNapi().xComponentDetachFlutterEngine(this.id) + } + } + + onAreaChange(newArea: Area | null, setFullScreen: boolean = false) { + const originalMetrics = this.viewportMetrics.clone(); + if (newArea != null) { + this.viewportMetrics.physicalWidth = vp2px(newArea.width as number); + this.viewportMetrics.physicalHeight = vp2px(newArea.height as number); + } + let fullScreen = false + // 根据是否全屏显示,设置标题栏高度 + if (this.checkFullScreen && + (setFullScreen || FlutterManager.getInstance().getFullScreenListener().useFullScreen())) { // 全屏显示 + fullScreen = true + this.viewportMetrics.physicalViewPaddingTop = this.systemAvoidArea!.topRect.height + this.viewportMetrics.physicalViewPaddingBottom = this.systemAvoidArea!.bottomRect.height + } else { // 非全屏显示 + this.viewportMetrics.physicalViewPaddingTop = 0; + this.viewportMetrics.physicalViewPaddingBottom = 0; + } + + this.viewportMetrics.physicalViewPaddingLeft = this.systemAvoidArea!.leftRect.width + this.viewportMetrics.physicalViewPaddingRight = this.systemAvoidArea!.rightRect.width + + this.onKeyboardAreaChange(fullScreen) + this.onAiBarAreaChange(fullScreen) + this.onGestureAreaChange(fullScreen) + if (!this.viewportMetrics.isEqual(originalMetrics) || this.needSetViewport) { + if (!this.updateViewportMetrics()) { + this.needSetViewport = true; + } else { + this.needSetViewport = false; + } + } + } + + // At the bottom of the single-frame phone is an AI assistant wake-up bar + // The area can lead to confusing gestures in repetitive areas without handled properly + // physicalViewPaddingBottom is modified by this function which needs to be called after onKeyboardAreaChange + private onAiBarAreaChange(fullScreen: boolean = false) { + if (this.checkAiBar && this.navigationAvoidArea != null && fullScreen) { + this.viewportMetrics.physicalViewPaddingBottom = + Math.max(this.navigationAvoidArea.bottomRect.height, this.viewportMetrics.physicalViewPaddingBottom) + } + } + + private onKeyboardAreaChange(fullScreen: boolean = false) { + if (this.checkKeyboard && fullScreen) { + this.viewportMetrics.physicalViewInsetTop = this.keyboardAvoidArea!.topRect.height + this.viewportMetrics.physicalViewInsetLeft = this.keyboardAvoidArea!.leftRect.width + this.viewportMetrics.physicalViewInsetBottom = this.keyboardAvoidArea!.bottomRect.height + this.viewportMetrics.physicalViewInsetRight = this.keyboardAvoidArea!.rightRect.width + } else { + this.viewportMetrics.physicalViewInsetTop = 0 + this.viewportMetrics.physicalViewInsetLeft = 0 + this.viewportMetrics.physicalViewInsetBottom = 0 + this.viewportMetrics.physicalViewInsetRight = 0 + } + } + + private onGestureAreaChange(fullScreen: boolean = false) { + if (this.checkGesture && fullScreen) { + this.viewportMetrics.systemGestureInsetTop = this.gestureAvoidArea!.topRect.height + this.viewportMetrics.systemGestureInsetLeft = this.gestureAvoidArea!.leftRect.width + this.viewportMetrics.systemGestureInsetBottom = + Math.max(this.navigationAvoidArea.bottomRect.height, this.gestureAvoidArea!.bottomRect.height) + this.viewportMetrics.systemGestureInsetRight = this.gestureAvoidArea!.rightRect.width + } else { + this.viewportMetrics.systemGestureInsetTop = 0 + this.viewportMetrics.systemGestureInsetLeft = 0 + this.viewportMetrics.systemGestureInsetBottom = 0 + this.viewportMetrics.systemGestureInsetRight = 0 + } + } + + public isAttachedToFlutterEngine(): boolean { + return this.flutterEngine != null + } + + public isSameEngineShellHolderId(id: number): boolean { + if (this.flutterEngine) { + let flutterNapi = this.flutterEngine.getFlutterNapi(); + if (flutterNapi.nativeShellHolderId == id && id != 0) { + return true; + } + } + return false; + } + + private updateViewportMetrics(): boolean { + if (this.isAttachedToFlutterEngine()) { + + const displayFeatures = this.viewportMetrics.displayFeatures; + let boundCount = 0; + for (let i = 0; i < displayFeatures.length; i++) { + boundCount = boundCount + displayFeatures[i].getBounds().length; + } + let displayFeatureBound: number[] = new Array(boundCount * 4); + let displayFeatureType: number[] = new Array(displayFeatures.length); + let displayFeatureStatus: number[] = new Array(displayFeatures.length); + for (let i = 0; i < displayFeatures.length; i++) { + let singleFeatureBound = displayFeatures[i].getBounds(); + for (let j = 0; j < singleFeatureBound.length; j++) { + displayFeatureBound[4 * i + 4 * j] = singleFeatureBound[j].left; + displayFeatureBound[4 * i + 4 * j + 1] = singleFeatureBound[j].top + displayFeatureBound[4 * i + 4 * j + 2] = singleFeatureBound[j].width; + displayFeatureBound[4 * i + 4 * j + 3] = singleFeatureBound[j].height; + } + displayFeatureType[i] = displayFeatures[i].getType(); + displayFeatureStatus[i] = displayFeatures[i].getState(); + } + + this?.flutterEngine?.getFlutterNapi()?.setViewportMetrics(this.viewportMetrics.devicePixelRatio, + this.viewportMetrics.physicalWidth, + this.viewportMetrics.physicalHeight, + this.viewportMetrics.physicalViewPaddingTop, + this.viewportMetrics.physicalViewPaddingRight, + this.viewportMetrics.physicalViewPaddingBottom, + this.viewportMetrics.physicalViewPaddingLeft, + this.viewportMetrics.physicalViewInsetTop, + this.viewportMetrics.physicalViewInsetRight, + this.viewportMetrics.physicalViewInsetBottom, + this.viewportMetrics.physicalViewInsetLeft, + this.viewportMetrics.systemGestureInsetTop, + this.viewportMetrics.systemGestureInsetRight, + this.viewportMetrics.systemGestureInsetBottom, + this.viewportMetrics.systemGestureInsetLeft, + this.viewportMetrics.physicalTouchSlop, + displayFeatureBound, + displayFeatureType, + displayFeatureStatus) + return true + } + return false + } + + onKeyPreIme(event: KeyEvent): boolean { + return this.keyboardManager?.onKeyPreIme(event) ?? false; + } + + onKeyEvent(event: KeyEvent): boolean { + return this.keyboardManager?.onKeyEvent(event) ?? false; + } + + onMouseWheel(eventType: string, event: PanGestureEvent) { + this.flutterEngine?.getFlutterNapi()?.xComponentDisPatchMouseWheel(this.id, eventType, event); + } + + addFirstFrameListener(listener: FirstFrameListener) { + this.mFirstFrameListeners.add(listener); + } + + removeFirstFrameListener(listener: FirstFrameListener) { + this.mFirstFrameListeners.remove(listener); + } + + addFirstPreloadFrameListener(listener: FirstPreloadFrameListener) { + this.mFirstPreloadFrameListeners.add(listener); + } + + removeFirstPreloadFrameListener(listener: FirstPreloadFrameListener) { + this.mFirstPreloadFrameListeners.remove(listener); + } + + hasRenderedFirstFrame(): boolean { + return this.isFlutterUiDisplayed; + } + + onFirstFrame(isPreload: number = 0) { + if (isPreload) { + let listeners = this.mFirstPreloadFrameListeners.clone(); + listeners.forEach((listener) => { + listener.onFirstPreloadFrame(); + }) + } else { + let listeners = this.mFirstFrameListeners.clone(); + listeners.forEach((listener) => { + listener.onFirstFrame(); + }) + } + } + + setCheckFullScreen(check: boolean) { + this.checkFullScreen = check; + } + + setCheckKeyboard(check: boolean) { + this.checkKeyboard = check + } + + setCheckGesture(check: boolean) { + this.checkGesture = check + } + + setCheckAiBar(check: boolean) { + this.checkAiBar = check + } + + enableFrameCache(cacheEnable: boolean) { + this.frameCache = cacheEnable; + if (this.isAttachedToFlutterEngine()) { + this.flutterEngine?.getFlutterNapi().enableFrameCache(cacheEnable); + } + } +} + +export interface FirstFrameListener { + onFirstFrame(): void; +} + +export interface FirstPreloadFrameListener { + onFirstPreloadFrame(): void; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/TextureRegistry.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/TextureRegistry.ets new file mode 100644 index 0000000000000000000000000000000000000000..e41e3fb63ac18993f87d1226ac1975d9862b07cd --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/TextureRegistry.ets @@ -0,0 +1,61 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import image from '@ohos.multimedia.image'; + +export interface TextureRegistry { + + getTextureId(): number; + + registerTexture(textureId: number): SurfaceTextureEntry; + + registerPixelMap(pixelMap: PixelMap): number; + + setTextureBackGroundPixelMap(textureId: number, pixelMap: PixelMap): void; + + setTextureBufferSize(textureId: number, width: number, height: number): void; + + notifyTextureResizing(textureId: number, width: number, height: number): void; + + setExternalNativeImage(textureId: number, native_image: number): boolean; + + resetExternalTexture(textureId: number, need_surfaceId: boolean): number; + + unregisterTexture(textureId: number): void; + + onTrimMemory(level: number): void; +} + +export interface SurfaceTextureEntry { + getTextureId(): number; + + getSurfaceId(): number; + + /* + * This return value is OHNativeWindow* in native code. + * Once converted to OHNativeWindow*, it can be used to create an EGLSurface or VkSurface for rendering. + * This OHNativeWindow* needn't be released when invoking unregisterTexture. + */ + getNativeWindowId(): number; + + release(): void; +} + +export interface OnFrameConsumedListener { + onFrameConsumed(): void; +} + +export interface OnTrimMemoryListener { + onTrimMemory(level: number): void; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/module.json5 b/shell/platform/ohos/flutter_embedding/flutter/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..61db0f5caed1a2eeec6fc991771d07d842f762d6 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/module.json5 @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +{ + "module": { + "name": "flutter", + "type": "har", + "deviceTypes": [ + "default" + ] + } +} diff --git a/shell/platform/ohos/flutter_embedding/hvigor/hvigor-config.json5 b/shell/platform/ohos/flutter_embedding/hvigor/hvigor-config.json5 new file mode 100755 index 0000000000000000000000000000000000000000..ebe4ba53b079cf5966e7f69c5a98029467543db6 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/hvigor/hvigor-config.json5 @@ -0,0 +1,20 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +{ + "modelVersion": "5.0.0", + "dependencies": { + } +} diff --git a/shell/platform/ohos/flutter_embedding/hvigor/hvigor-wrapper.js b/shell/platform/ohos/flutter_embedding/hvigor/hvigor-wrapper.js new file mode 100755 index 0000000000000000000000000000000000000000..1b83395c99abd4b8ff29ed95b4df9aae8685d660 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/hvigor/hvigor-wrapper.js @@ -0,0 +1 @@ +"use strict";var e=require("path"),t=require("os"),n=require("fs"),r=require("child_process"),u=require("constants"),o=require("stream"),i=require("util"),s=require("assert"),c=require("process"),a=require("tty"),l=require("url"),f=require("zlib"),d=require("net"),D=require("crypto"),p="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},h={},E={},m=p&&p.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(E,"__esModule",{value:!0}),E.maxPathLength=E.isMac=E.isLinux=E.isWindows=void 0;const y=m(t),C="Windows_NT",F="Darwin";function g(){return y.default.type()===C}function A(){return y.default.type()===F}E.isWindows=g,E.isLinux=function(){return"Linux"===y.default.type()},E.isMac=A,E.maxPathLength=function(){return A()?1016:g()?259:4095},Object.defineProperty(h,"__esModule",{value:!0}),h.LOG_LEVEL=h.ANALYZE=h.PARALLEL=h.INCREMENTAL=h.DAEMON=h.DOT=h.PROPERTIES=h.OHOS_ARK_COMPILE_MAX_SIZE=h.HVIGOR_POOL_CACHE_TTL=h.HVIGOR_POOL_CACHE_CAPACITY=h.HVIGOR_POOL_MAX_CORE_SIZE=h.HVIGOR_POOL_MAX_SIZE=h.BUILD_CACHE_DIR=h.ENABLE_SIGN_TASK_KEY=h.HVIGOR_CACHE_DIR_KEY=h.WORK_SPACE=h.PROJECT_CACHES=h.HVIGOR_USER_HOME_DIR_NAME=h.DEFAULT_PACKAGE_JSON=h.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME=h.PNPM=h.HVIGOR=h.NPM_TOOL=h.PNPM_TOOL=h.HVIGOR_ENGINE_PACKAGE_NAME=void 0;const v=E;h.HVIGOR_ENGINE_PACKAGE_NAME="@ohos/hvigor",h.PNPM_TOOL=(0,v.isWindows)()?"pnpm.cmd":"pnpm",h.NPM_TOOL=(0,v.isWindows)()?"npm.cmd":"npm",h.HVIGOR="hvigor",h.PNPM="pnpm",h.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME="hvigor-config.json5",h.DEFAULT_PACKAGE_JSON="package.json",h.HVIGOR_USER_HOME_DIR_NAME=".hvigor",h.PROJECT_CACHES="project_caches";var S=h.WORK_SPACE="workspace";h.HVIGOR_CACHE_DIR_KEY="hvigor.cacheDir",h.ENABLE_SIGN_TASK_KEY="enableSignTask",h.BUILD_CACHE_DIR="build-cache-dir",h.HVIGOR_POOL_MAX_SIZE="hvigor.pool.maxSize",h.HVIGOR_POOL_MAX_CORE_SIZE="hvigor.pool.maxCoreSize",h.HVIGOR_POOL_CACHE_CAPACITY="hvigor.pool.cache.capacity",h.HVIGOR_POOL_CACHE_TTL="hvigor.pool.cache.ttl",h.OHOS_ARK_COMPILE_MAX_SIZE="ohos.arkCompile.maxSize",h.PROPERTIES="properties",h.DOT=".",h.DAEMON="daemon",h.INCREMENTAL="incremental",h.PARALLEL="typeCheck",h.ANALYZE="analyze",h.LOG_LEVEL="logLevel";var w={},O={};Object.defineProperty(O,"__esModule",{value:!0}),O.logError=O.logInfo=O.logErrorAndExit=void 0,O.logErrorAndExit=function(e){e instanceof Error?console.error(e.message):console.error(e),process.exit(-1)},O.logInfo=function(e){console.log(e)},O.logError=function(e){console.error(e)};var _=p&&p.__createBinding||(Object.create?function(e,t,n,r){void 0===r&&(r=n);var u=Object.getOwnPropertyDescriptor(t,n);u&&!("get"in u?!t.__esModule:u.writable||u.configurable)||(u={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,u)}:function(e,t,n,r){void 0===r&&(r=n),e[r]=t[n]}),b=p&&p.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),B=p&&p.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.prototype.hasOwnProperty.call(e,n)&&_(t,e,n);return b(t,e),t};Object.defineProperty(w,"__esModule",{value:!0});var P=w.executeBuild=void 0;const I=B(n),k=B(e),x=O,N=r;P=w.executeBuild=function(e){const t=k.resolve(e,"node_modules","@ohos","hvigor","bin","hvigor.js");try{const e=I.realpathSync(t),n=process.argv.slice(2),r=(0,N.spawn)("node",[e,...n],{env:process.env});r.stdout.on("data",(e=>{(0,x.logInfo)(`${e.toString().trim()}`)})),r.stderr.on("data",(e=>{(0,x.logError)(`${e.toString().trim()}`)})),r.on("exit",((e,t)=>{process.exit(null!=e?e:-1)}))}catch(n){(0,x.logErrorAndExit)(`Error: ENOENT: no such file ${t},delete ${e} and retry.`)}};var R={},T={},M={fromCallback:function(e){return Object.defineProperty((function(...t){if("function"!=typeof t[t.length-1])return new Promise(((n,r)=>{t.push(((e,t)=>null!=e?r(e):n(t))),e.apply(this,t)}));e.apply(this,t)}),"name",{value:e.name})},fromPromise:function(e){return Object.defineProperty((function(...t){const n=t[t.length-1];if("function"!=typeof n)return e.apply(this,t);t.pop(),e.apply(this,t).then((e=>n(null,e)),n)}),"name",{value:e.name})}},L=u,j=process.cwd,$=null,H=process.env.GRACEFUL_FS_PLATFORM||process.platform;process.cwd=function(){return $||($=j.call(process)),$};try{process.cwd()}catch(e){}if("function"==typeof process.chdir){var G=process.chdir;process.chdir=function(e){$=null,G.call(process,e)},Object.setPrototypeOf&&Object.setPrototypeOf(process.chdir,G)}var V=function(e){L.hasOwnProperty("O_SYMLINK")&&process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)&&function(e){e.lchmod=function(t,n,r){e.open(t,L.O_WRONLY|L.O_SYMLINK,n,(function(t,u){t?r&&r(t):e.fchmod(u,n,(function(t){e.close(u,(function(e){r&&r(t||e)}))}))}))},e.lchmodSync=function(t,n){var r,u=e.openSync(t,L.O_WRONLY|L.O_SYMLINK,n),o=!0;try{r=e.fchmodSync(u,n),o=!1}finally{if(o)try{e.closeSync(u)}catch(e){}else e.closeSync(u)}return r}}(e);e.lutimes||function(e){L.hasOwnProperty("O_SYMLINK")&&e.futimes?(e.lutimes=function(t,n,r,u){e.open(t,L.O_SYMLINK,(function(t,o){t?u&&u(t):e.futimes(o,n,r,(function(t){e.close(o,(function(e){u&&u(t||e)}))}))}))},e.lutimesSync=function(t,n,r){var u,o=e.openSync(t,L.O_SYMLINK),i=!0;try{u=e.futimesSync(o,n,r),i=!1}finally{if(i)try{e.closeSync(o)}catch(e){}else e.closeSync(o)}return u}):e.futimes&&(e.lutimes=function(e,t,n,r){r&&process.nextTick(r)},e.lutimesSync=function(){})}(e);e.chown=r(e.chown),e.fchown=r(e.fchown),e.lchown=r(e.lchown),e.chmod=t(e.chmod),e.fchmod=t(e.fchmod),e.lchmod=t(e.lchmod),e.chownSync=u(e.chownSync),e.fchownSync=u(e.fchownSync),e.lchownSync=u(e.lchownSync),e.chmodSync=n(e.chmodSync),e.fchmodSync=n(e.fchmodSync),e.lchmodSync=n(e.lchmodSync),e.stat=o(e.stat),e.fstat=o(e.fstat),e.lstat=o(e.lstat),e.statSync=i(e.statSync),e.fstatSync=i(e.fstatSync),e.lstatSync=i(e.lstatSync),e.chmod&&!e.lchmod&&(e.lchmod=function(e,t,n){n&&process.nextTick(n)},e.lchmodSync=function(){});e.chown&&!e.lchown&&(e.lchown=function(e,t,n,r){r&&process.nextTick(r)},e.lchownSync=function(){});"win32"===H&&(e.rename="function"!=typeof e.rename?e.rename:function(t){function n(n,r,u){var o=Date.now(),i=0;t(n,r,(function s(c){if(c&&("EACCES"===c.code||"EPERM"===c.code||"EBUSY"===c.code)&&Date.now()-o<6e4)return setTimeout((function(){e.stat(r,(function(e,o){e&&"ENOENT"===e.code?t(n,r,s):u(c)}))}),i),void(i<100&&(i+=10));u&&u(c)}))}return Object.setPrototypeOf&&Object.setPrototypeOf(n,t),n}(e.rename));function t(t){return t?function(n,r,u){return t.call(e,n,r,(function(e){s(e)&&(e=null),u&&u.apply(this,arguments)}))}:t}function n(t){return t?function(n,r){try{return t.call(e,n,r)}catch(e){if(!s(e))throw e}}:t}function r(t){return t?function(n,r,u,o){return t.call(e,n,r,u,(function(e){s(e)&&(e=null),o&&o.apply(this,arguments)}))}:t}function u(t){return t?function(n,r,u){try{return t.call(e,n,r,u)}catch(e){if(!s(e))throw e}}:t}function o(t){return t?function(n,r,u){function o(e,t){t&&(t.uid<0&&(t.uid+=4294967296),t.gid<0&&(t.gid+=4294967296)),u&&u.apply(this,arguments)}return"function"==typeof r&&(u=r,r=null),r?t.call(e,n,r,o):t.call(e,n,o)}:t}function i(t){return t?function(n,r){var u=r?t.call(e,n,r):t.call(e,n);return u&&(u.uid<0&&(u.uid+=4294967296),u.gid<0&&(u.gid+=4294967296)),u}:t}function s(e){return!e||("ENOSYS"===e.code||!(process.getuid&&0===process.getuid()||"EINVAL"!==e.code&&"EPERM"!==e.code))}e.read="function"!=typeof e.read?e.read:function(t){function n(n,r,u,o,i,s){var c;if(s&&"function"==typeof s){var a=0;c=function(l,f,d){if(l&&"EAGAIN"===l.code&&a<10)return a++,t.call(e,n,r,u,o,i,c);s.apply(this,arguments)}}return t.call(e,n,r,u,o,i,c)}return Object.setPrototypeOf&&Object.setPrototypeOf(n,t),n}(e.read),e.readSync="function"!=typeof e.readSync?e.readSync:(c=e.readSync,function(t,n,r,u,o){for(var i=0;;)try{return c.call(e,t,n,r,u,o)}catch(e){if("EAGAIN"===e.code&&i<10){i++;continue}throw e}});var c};var U=o.Stream,J=function(e){return{ReadStream:function t(n,r){if(!(this instanceof t))return new t(n,r);U.call(this);var u=this;this.path=n,this.fd=null,this.readable=!0,this.paused=!1,this.flags="r",this.mode=438,this.bufferSize=65536,r=r||{};for(var o=Object.keys(r),i=0,s=o.length;ithis.end)throw new Error("start must be <= end");this.pos=this.start}if(null!==this.fd)return void process.nextTick((function(){u._read()}));e.open(this.path,this.flags,this.mode,(function(e,t){if(e)return u.emit("error",e),void(u.readable=!1);u.fd=t,u.emit("open",t),u._read()}))},WriteStream:function t(n,r){if(!(this instanceof t))return new t(n,r);U.call(this),this.path=n,this.fd=null,this.writable=!0,this.flags="w",this.encoding="binary",this.mode=438,this.bytesWritten=0,r=r||{};for(var u=Object.keys(r),o=0,i=u.length;o= zero");this.pos=this.start}this.busy=!1,this._queue=[],null===this.fd&&(this._open=e.open,this._queue.push([this._open,this.path,this.flags,this.mode,void 0]),this.flush())}}};var W=function(e){if(null===e||"object"!=typeof e)return e;if(e instanceof Object)var t={__proto__:z(e)};else t=Object.create(null);return Object.getOwnPropertyNames(e).forEach((function(n){Object.defineProperty(t,n,Object.getOwnPropertyDescriptor(e,n))})),t},z=Object.getPrototypeOf||function(e){return e.__proto__};var K,q,Y=n,Z=V,X=J,Q=W,ee=i;function te(e,t){Object.defineProperty(e,K,{get:function(){return t}})}"function"==typeof Symbol&&"function"==typeof Symbol.for?(K=Symbol.for("graceful-fs.queue"),q=Symbol.for("graceful-fs.previous")):(K="___graceful-fs.queue",q="___graceful-fs.previous");var ne=function(){};if(ee.debuglog?ne=ee.debuglog("gfs4"):/\bgfs4\b/i.test(process.env.NODE_DEBUG||"")&&(ne=function(){var e=ee.format.apply(ee,arguments);e="GFS4: "+e.split(/\n/).join("\nGFS4: "),console.error(e)}),!Y[K]){var re=p[K]||[];te(Y,re),Y.close=function(e){function t(t,n){return e.call(Y,t,(function(e){e||ce(),"function"==typeof n&&n.apply(this,arguments)}))}return Object.defineProperty(t,q,{value:e}),t}(Y.close),Y.closeSync=function(e){function t(t){e.apply(Y,arguments),ce()}return Object.defineProperty(t,q,{value:e}),t}(Y.closeSync),/\bgfs4\b/i.test(process.env.NODE_DEBUG||"")&&process.on("exit",(function(){ne(Y[K]),s.equal(Y[K].length,0)}))}p[K]||te(p,Y[K]);var ue,oe=ie(Q(Y));function ie(e){Z(e),e.gracefulify=ie,e.createReadStream=function(t,n){return new e.ReadStream(t,n)},e.createWriteStream=function(t,n){return new e.WriteStream(t,n)};var t=e.readFile;e.readFile=function(e,n,r){"function"==typeof n&&(r=n,n=null);return function e(n,r,u,o){return t(n,r,(function(t){!t||"EMFILE"!==t.code&&"ENFILE"!==t.code?"function"==typeof u&&u.apply(this,arguments):se([e,[n,r,u],t,o||Date.now(),Date.now()])}))}(e,n,r)};var n=e.writeFile;e.writeFile=function(e,t,r,u){"function"==typeof r&&(u=r,r=null);return function e(t,r,u,o,i){return n(t,r,u,(function(n){!n||"EMFILE"!==n.code&&"ENFILE"!==n.code?"function"==typeof o&&o.apply(this,arguments):se([e,[t,r,u,o],n,i||Date.now(),Date.now()])}))}(e,t,r,u)};var r=e.appendFile;r&&(e.appendFile=function(e,t,n,u){"function"==typeof n&&(u=n,n=null);return function e(t,n,u,o,i){return r(t,n,u,(function(r){!r||"EMFILE"!==r.code&&"ENFILE"!==r.code?"function"==typeof o&&o.apply(this,arguments):se([e,[t,n,u,o],r,i||Date.now(),Date.now()])}))}(e,t,n,u)});var u=e.copyFile;u&&(e.copyFile=function(e,t,n,r){"function"==typeof n&&(r=n,n=0);return function e(t,n,r,o,i){return u(t,n,r,(function(u){!u||"EMFILE"!==u.code&&"ENFILE"!==u.code?"function"==typeof o&&o.apply(this,arguments):se([e,[t,n,r,o],u,i||Date.now(),Date.now()])}))}(e,t,n,r)});var o=e.readdir;e.readdir=function(e,t,n){"function"==typeof t&&(n=t,t=null);var r=i.test(process.version)?function(e,t,n,r){return o(e,u(e,t,n,r))}:function(e,t,n,r){return o(e,t,u(e,t,n,r))};return r(e,t,n);function u(e,t,n,u){return function(o,i){!o||"EMFILE"!==o.code&&"ENFILE"!==o.code?(i&&i.sort&&i.sort(),"function"==typeof n&&n.call(this,o,i)):se([r,[e,t,n],o,u||Date.now(),Date.now()])}}};var i=/^v[0-5]\./;if("v0.8"===process.version.substr(0,4)){var s=X(e);d=s.ReadStream,D=s.WriteStream}var c=e.ReadStream;c&&(d.prototype=Object.create(c.prototype),d.prototype.open=function(){var e=this;h(e.path,e.flags,e.mode,(function(t,n){t?(e.autoClose&&e.destroy(),e.emit("error",t)):(e.fd=n,e.emit("open",n),e.read())}))});var a=e.WriteStream;a&&(D.prototype=Object.create(a.prototype),D.prototype.open=function(){var e=this;h(e.path,e.flags,e.mode,(function(t,n){t?(e.destroy(),e.emit("error",t)):(e.fd=n,e.emit("open",n))}))}),Object.defineProperty(e,"ReadStream",{get:function(){return d},set:function(e){d=e},enumerable:!0,configurable:!0}),Object.defineProperty(e,"WriteStream",{get:function(){return D},set:function(e){D=e},enumerable:!0,configurable:!0});var l=d;Object.defineProperty(e,"FileReadStream",{get:function(){return l},set:function(e){l=e},enumerable:!0,configurable:!0});var f=D;function d(e,t){return this instanceof d?(c.apply(this,arguments),this):d.apply(Object.create(d.prototype),arguments)}function D(e,t){return this instanceof D?(a.apply(this,arguments),this):D.apply(Object.create(D.prototype),arguments)}Object.defineProperty(e,"FileWriteStream",{get:function(){return f},set:function(e){f=e},enumerable:!0,configurable:!0});var p=e.open;function h(e,t,n,r){return"function"==typeof n&&(r=n,n=null),function e(t,n,r,u,o){return p(t,n,r,(function(i,s){!i||"EMFILE"!==i.code&&"ENFILE"!==i.code?"function"==typeof u&&u.apply(this,arguments):se([e,[t,n,r,u],i,o||Date.now(),Date.now()])}))}(e,t,n,r)}return e.open=h,e}function se(e){ne("ENQUEUE",e[0].name,e[1]),Y[K].push(e),ae()}function ce(){for(var e=Date.now(),t=0;t2&&(Y[K][t][3]=e,Y[K][t][4]=e);ae()}function ae(){if(clearTimeout(ue),ue=void 0,0!==Y[K].length){var e=Y[K].shift(),t=e[0],n=e[1],r=e[2],u=e[3],o=e[4];if(void 0===u)ne("RETRY",t.name,n),t.apply(null,n);else if(Date.now()-u>=6e4){ne("TIMEOUT",t.name,n);var i=n.pop();"function"==typeof i&&i.call(null,r)}else{var s=Date.now()-o,c=Math.max(o-u,1);s>=Math.min(1.2*c,100)?(ne("RETRY",t.name,n),t.apply(null,n.concat([u]))):Y[K].push(e)}void 0===ue&&(ue=setTimeout(ae,0))}}process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH&&!Y.__patched&&(oe=ie(Y),Y.__patched=!0),function(e){const t=M.fromCallback,n=oe,r=["access","appendFile","chmod","chown","close","copyFile","fchmod","fchown","fdatasync","fstat","fsync","ftruncate","futimes","lchmod","lchown","link","lstat","mkdir","mkdtemp","open","opendir","readdir","readFile","readlink","realpath","rename","rm","rmdir","stat","symlink","truncate","unlink","utimes","writeFile"].filter((e=>"function"==typeof n[e]));Object.assign(e,n),r.forEach((r=>{e[r]=t(n[r])})),e.exists=function(e,t){return"function"==typeof t?n.exists(e,t):new Promise((t=>n.exists(e,t)))},e.read=function(e,t,r,u,o,i){return"function"==typeof i?n.read(e,t,r,u,o,i):new Promise(((i,s)=>{n.read(e,t,r,u,o,((e,t,n)=>{if(e)return s(e);i({bytesRead:t,buffer:n})}))}))},e.write=function(e,t,...r){return"function"==typeof r[r.length-1]?n.write(e,t,...r):new Promise(((u,o)=>{n.write(e,t,...r,((e,t,n)=>{if(e)return o(e);u({bytesWritten:t,buffer:n})}))}))},e.readv=function(e,t,...r){return"function"==typeof r[r.length-1]?n.readv(e,t,...r):new Promise(((u,o)=>{n.readv(e,t,...r,((e,t,n)=>{if(e)return o(e);u({bytesRead:t,buffers:n})}))}))},e.writev=function(e,t,...r){return"function"==typeof r[r.length-1]?n.writev(e,t,...r):new Promise(((u,o)=>{n.writev(e,t,...r,((e,t,n)=>{if(e)return o(e);u({bytesWritten:t,buffers:n})}))}))},"function"==typeof n.realpath.native?e.realpath.native=t(n.realpath.native):process.emitWarning("fs.realpath.native is not a function. Is fs being monkey-patched?","Warning","fs-extra-WARN0003")}(T);var le={},fe={};const de=e;fe.checkPath=function(e){if("win32"===process.platform){if(/[<>:"|?*]/.test(e.replace(de.parse(e).root,""))){const t=new Error(`Path contains invalid characters: ${e}`);throw t.code="EINVAL",t}}};const De=T,{checkPath:pe}=fe,he=e=>"number"==typeof e?e:{mode:511,...e}.mode;le.makeDir=async(e,t)=>(pe(e),De.mkdir(e,{mode:he(t),recursive:!0})),le.makeDirSync=(e,t)=>(pe(e),De.mkdirSync(e,{mode:he(t),recursive:!0}));const Ee=M.fromPromise,{makeDir:me,makeDirSync:ye}=le,Ce=Ee(me);var Fe={mkdirs:Ce,mkdirsSync:ye,mkdirp:Ce,mkdirpSync:ye,ensureDir:Ce,ensureDirSync:ye};const ge=M.fromPromise,Ae=T;var ve={pathExists:ge((function(e){return Ae.access(e).then((()=>!0)).catch((()=>!1))})),pathExistsSync:Ae.existsSync};const Se=oe;var we=function(e,t,n,r){Se.open(e,"r+",((e,u)=>{if(e)return r(e);Se.futimes(u,t,n,(e=>{Se.close(u,(t=>{r&&r(e||t)}))}))}))},Oe=function(e,t,n){const r=Se.openSync(e,"r+");return Se.futimesSync(r,t,n),Se.closeSync(r)};const _e=T,be=e,Be=i;function Pe(e,t,n){const r=n.dereference?e=>_e.stat(e,{bigint:!0}):e=>_e.lstat(e,{bigint:!0});return Promise.all([r(e),r(t).catch((e=>{if("ENOENT"===e.code)return null;throw e}))]).then((([e,t])=>({srcStat:e,destStat:t})))}function Ie(e,t){return t.ino&&t.dev&&t.ino===e.ino&&t.dev===e.dev}function ke(e,t){const n=be.resolve(e).split(be.sep).filter((e=>e)),r=be.resolve(t).split(be.sep).filter((e=>e));return n.reduce(((e,t,n)=>e&&r[n]===t),!0)}function xe(e,t,n){return`Cannot ${n} '${e}' to a subdirectory of itself, '${t}'.`}var Ne={checkPaths:function(e,t,n,r,u){Be.callbackify(Pe)(e,t,r,((r,o)=>{if(r)return u(r);const{srcStat:i,destStat:s}=o;if(s){if(Ie(i,s)){const r=be.basename(e),o=be.basename(t);return"move"===n&&r!==o&&r.toLowerCase()===o.toLowerCase()?u(null,{srcStat:i,destStat:s,isChangingCase:!0}):u(new Error("Source and destination must not be the same."))}if(i.isDirectory()&&!s.isDirectory())return u(new Error(`Cannot overwrite non-directory '${t}' with directory '${e}'.`));if(!i.isDirectory()&&s.isDirectory())return u(new Error(`Cannot overwrite directory '${t}' with non-directory '${e}'.`))}return i.isDirectory()&&ke(e,t)?u(new Error(xe(e,t,n))):u(null,{srcStat:i,destStat:s})}))},checkPathsSync:function(e,t,n,r){const{srcStat:u,destStat:o}=function(e,t,n){let r;const u=n.dereference?e=>_e.statSync(e,{bigint:!0}):e=>_e.lstatSync(e,{bigint:!0}),o=u(e);try{r=u(t)}catch(e){if("ENOENT"===e.code)return{srcStat:o,destStat:null};throw e}return{srcStat:o,destStat:r}}(e,t,r);if(o){if(Ie(u,o)){const r=be.basename(e),i=be.basename(t);if("move"===n&&r!==i&&r.toLowerCase()===i.toLowerCase())return{srcStat:u,destStat:o,isChangingCase:!0};throw new Error("Source and destination must not be the same.")}if(u.isDirectory()&&!o.isDirectory())throw new Error(`Cannot overwrite non-directory '${t}' with directory '${e}'.`);if(!u.isDirectory()&&o.isDirectory())throw new Error(`Cannot overwrite directory '${t}' with non-directory '${e}'.`)}if(u.isDirectory()&&ke(e,t))throw new Error(xe(e,t,n));return{srcStat:u,destStat:o}},checkParentPaths:function e(t,n,r,u,o){const i=be.resolve(be.dirname(t)),s=be.resolve(be.dirname(r));if(s===i||s===be.parse(s).root)return o();_e.stat(s,{bigint:!0},((i,c)=>i?"ENOENT"===i.code?o():o(i):Ie(n,c)?o(new Error(xe(t,r,u))):e(t,n,s,u,o)))},checkParentPathsSync:function e(t,n,r,u){const o=be.resolve(be.dirname(t)),i=be.resolve(be.dirname(r));if(i===o||i===be.parse(i).root)return;let s;try{s=_e.statSync(i,{bigint:!0})}catch(e){if("ENOENT"===e.code)return;throw e}if(Ie(n,s))throw new Error(xe(t,r,u));return e(t,n,i,u)},isSrcSubdir:ke,areIdentical:Ie};const Re=oe,Te=e,Me=Fe.mkdirs,Le=ve.pathExists,je=we,$e=Ne;function He(e,t,n,r){if(!n.filter)return r(null,!0);Promise.resolve(n.filter(e,t)).then((e=>r(null,e)),(e=>r(e)))}function Ge(e,t,n,r,u){(r.dereference?Re.stat:Re.lstat)(t,((o,i)=>o?u(o):i.isDirectory()?function(e,t,n,r,u,o){return t?We(n,r,u,o):function(e,t,n,r,u){Re.mkdir(n,(o=>{if(o)return u(o);We(t,n,r,(t=>t?u(t):Je(n,e,u)))}))}(e.mode,n,r,u,o)}(i,e,t,n,r,u):i.isFile()||i.isCharacterDevice()||i.isBlockDevice()?function(e,t,n,r,u,o){return t?function(e,t,n,r,u){if(!r.overwrite)return r.errorOnExist?u(new Error(`'${n}' already exists`)):u();Re.unlink(n,(o=>o?u(o):Ve(e,t,n,r,u)))}(e,n,r,u,o):Ve(e,n,r,u,o)}(i,e,t,n,r,u):i.isSymbolicLink()?function(e,t,n,r,u){Re.readlink(t,((t,o)=>t?u(t):(r.dereference&&(o=Te.resolve(process.cwd(),o)),e?void Re.readlink(n,((e,t)=>e?"EINVAL"===e.code||"UNKNOWN"===e.code?Re.symlink(o,n,u):u(e):(r.dereference&&(t=Te.resolve(process.cwd(),t)),$e.isSrcSubdir(o,t)?u(new Error(`Cannot copy '${o}' to a subdirectory of itself, '${t}'.`)):$e.isSrcSubdir(t,o)?u(new Error(`Cannot overwrite '${t}' with '${o}'.`)):function(e,t,n){Re.unlink(t,(r=>r?n(r):Re.symlink(e,t,n)))}(o,n,u)))):Re.symlink(o,n,u))))}(e,t,n,r,u):i.isSocket()?u(new Error(`Cannot copy a socket file: ${t}`)):i.isFIFO()?u(new Error(`Cannot copy a FIFO pipe: ${t}`)):u(new Error(`Unknown file: ${t}`))))}function Ve(e,t,n,r,u){Re.copyFile(t,n,(o=>o?u(o):r.preserveTimestamps?function(e,t,n,r){if(function(e){return!(128&e)}(e))return function(e,t,n){return Je(e,128|t,n)}(n,e,(u=>u?r(u):Ue(e,t,n,r)));return Ue(e,t,n,r)}(e.mode,t,n,u):Je(n,e.mode,u)))}function Ue(e,t,n,r){!function(e,t,n){Re.stat(e,((e,r)=>e?n(e):je(t,r.atime,r.mtime,n)))}(t,n,(t=>t?r(t):Je(n,e,r)))}function Je(e,t,n){return Re.chmod(e,t,n)}function We(e,t,n,r){Re.readdir(e,((u,o)=>u?r(u):ze(o,e,t,n,r)))}function ze(e,t,n,r,u){const o=e.pop();return o?function(e,t,n,r,u,o){const i=Te.join(n,t),s=Te.join(r,t);He(i,s,u,((t,c)=>t?o(t):c?void $e.checkPaths(i,s,"copy",u,((t,c)=>{if(t)return o(t);const{destStat:a}=c;Ge(a,i,s,u,(t=>t?o(t):ze(e,n,r,u,o)))})):ze(e,n,r,u,o)))}(e,o,t,n,r,u):u()}var Ke=function(e,t,n,r){"function"!=typeof n||r?"function"==typeof n&&(n={filter:n}):(r=n,n={}),r=r||function(){},(n=n||{}).clobber=!("clobber"in n)||!!n.clobber,n.overwrite="overwrite"in n?!!n.overwrite:n.clobber,n.preserveTimestamps&&"ia32"===process.arch&&process.emitWarning("Using the preserveTimestamps option in 32-bit node is not recommended;\n\n\tsee https://github.com/jprichardson/node-fs-extra/issues/269","Warning","fs-extra-WARN0001"),$e.checkPaths(e,t,"copy",n,((u,o)=>{if(u)return r(u);const{srcStat:i,destStat:s}=o;$e.checkParentPaths(e,i,t,"copy",(u=>{if(u)return r(u);He(e,t,n,((u,o)=>u?r(u):o?void function(e,t,n,r,u){const o=Te.dirname(n);Le(o,((i,s)=>i?u(i):s?Ge(e,t,n,r,u):void Me(o,(o=>o?u(o):Ge(e,t,n,r,u)))))}(s,e,t,n,r):r()))}))}))};const qe=oe,Ye=e,Ze=Fe.mkdirsSync,Xe=Oe,Qe=Ne;function et(e,t,n,r){const u=(r.dereference?qe.statSync:qe.lstatSync)(t);if(u.isDirectory())return function(e,t,n,r,u){return t?rt(n,r,u):function(e,t,n,r){return qe.mkdirSync(n),rt(t,n,r),nt(n,e)}(e.mode,n,r,u)}(u,e,t,n,r);if(u.isFile()||u.isCharacterDevice()||u.isBlockDevice())return function(e,t,n,r,u){return t?function(e,t,n,r){if(r.overwrite)return qe.unlinkSync(n),tt(e,t,n,r);if(r.errorOnExist)throw new Error(`'${n}' already exists`)}(e,n,r,u):tt(e,n,r,u)}(u,e,t,n,r);if(u.isSymbolicLink())return function(e,t,n,r){let u=qe.readlinkSync(t);r.dereference&&(u=Ye.resolve(process.cwd(),u));if(e){let e;try{e=qe.readlinkSync(n)}catch(e){if("EINVAL"===e.code||"UNKNOWN"===e.code)return qe.symlinkSync(u,n);throw e}if(r.dereference&&(e=Ye.resolve(process.cwd(),e)),Qe.isSrcSubdir(u,e))throw new Error(`Cannot copy '${u}' to a subdirectory of itself, '${e}'.`);if(Qe.isSrcSubdir(e,u))throw new Error(`Cannot overwrite '${e}' with '${u}'.`);return function(e,t){return qe.unlinkSync(t),qe.symlinkSync(e,t)}(u,n)}return qe.symlinkSync(u,n)}(e,t,n,r);if(u.isSocket())throw new Error(`Cannot copy a socket file: ${t}`);if(u.isFIFO())throw new Error(`Cannot copy a FIFO pipe: ${t}`);throw new Error(`Unknown file: ${t}`)}function tt(e,t,n,r){return qe.copyFileSync(t,n),r.preserveTimestamps&&function(e,t,n){(function(e){return!(128&e)})(e)&&function(e,t){nt(e,128|t)}(n,e);(function(e,t){const n=qe.statSync(e);Xe(t,n.atime,n.mtime)})(t,n)}(e.mode,t,n),nt(n,e.mode)}function nt(e,t){return qe.chmodSync(e,t)}function rt(e,t,n){qe.readdirSync(e).forEach((r=>function(e,t,n,r){const u=Ye.join(t,e),o=Ye.join(n,e);if(r.filter&&!r.filter(u,o))return;const{destStat:i}=Qe.checkPathsSync(u,o,"copy",r);return et(i,u,o,r)}(r,e,t,n)))}var ut=function(e,t,n){"function"==typeof n&&(n={filter:n}),(n=n||{}).clobber=!("clobber"in n)||!!n.clobber,n.overwrite="overwrite"in n?!!n.overwrite:n.clobber,n.preserveTimestamps&&"ia32"===process.arch&&process.emitWarning("Using the preserveTimestamps option in 32-bit node is not recommended;\n\n\tsee https://github.com/jprichardson/node-fs-extra/issues/269","Warning","fs-extra-WARN0002");const{srcStat:r,destStat:u}=Qe.checkPathsSync(e,t,"copy",n);if(Qe.checkParentPathsSync(e,r,t,"copy"),n.filter&&!n.filter(e,t))return;const o=Ye.dirname(t);return qe.existsSync(o)||Ze(o),et(u,e,t,n)};var ot={copy:(0,M.fromCallback)(Ke),copySync:ut};const it=oe;var st={remove:(0,M.fromCallback)((function(e,t){it.rm(e,{recursive:!0,force:!0},t)})),removeSync:function(e){it.rmSync(e,{recursive:!0,force:!0})}};const ct=M.fromPromise,at=T,lt=e,ft=Fe,dt=st,Dt=ct((async function(e){let t;try{t=await at.readdir(e)}catch{return ft.mkdirs(e)}return Promise.all(t.map((t=>dt.remove(lt.join(e,t)))))}));function pt(e){let t;try{t=at.readdirSync(e)}catch{return ft.mkdirsSync(e)}t.forEach((t=>{t=lt.join(e,t),dt.removeSync(t)}))}var ht={emptyDirSync:pt,emptydirSync:pt,emptyDir:Dt,emptydir:Dt};const Et=M.fromCallback,mt=e,yt=oe,Ct=Fe;var Ft={createFile:Et((function(e,t){function n(){yt.writeFile(e,"",(e=>{if(e)return t(e);t()}))}yt.stat(e,((r,u)=>{if(!r&&u.isFile())return t();const o=mt.dirname(e);yt.stat(o,((e,r)=>{if(e)return"ENOENT"===e.code?Ct.mkdirs(o,(e=>{if(e)return t(e);n()})):t(e);r.isDirectory()?n():yt.readdir(o,(e=>{if(e)return t(e)}))}))}))})),createFileSync:function(e){let t;try{t=yt.statSync(e)}catch{}if(t&&t.isFile())return;const n=mt.dirname(e);try{yt.statSync(n).isDirectory()||yt.readdirSync(n)}catch(e){if(!e||"ENOENT"!==e.code)throw e;Ct.mkdirsSync(n)}yt.writeFileSync(e,"")}};const gt=M.fromCallback,At=e,vt=oe,St=Fe,wt=ve.pathExists,{areIdentical:Ot}=Ne;var _t={createLink:gt((function(e,t,n){function r(e,t){vt.link(e,t,(e=>{if(e)return n(e);n(null)}))}vt.lstat(t,((u,o)=>{vt.lstat(e,((u,i)=>{if(u)return u.message=u.message.replace("lstat","ensureLink"),n(u);if(o&&Ot(i,o))return n(null);const s=At.dirname(t);wt(s,((u,o)=>u?n(u):o?r(e,t):void St.mkdirs(s,(u=>{if(u)return n(u);r(e,t)}))))}))}))})),createLinkSync:function(e,t){let n;try{n=vt.lstatSync(t)}catch{}try{const t=vt.lstatSync(e);if(n&&Ot(t,n))return}catch(e){throw e.message=e.message.replace("lstat","ensureLink"),e}const r=At.dirname(t);return vt.existsSync(r)||St.mkdirsSync(r),vt.linkSync(e,t)}};const bt=e,Bt=oe,Pt=ve.pathExists;var It={symlinkPaths:function(e,t,n){if(bt.isAbsolute(e))return Bt.lstat(e,(t=>t?(t.message=t.message.replace("lstat","ensureSymlink"),n(t)):n(null,{toCwd:e,toDst:e})));{const r=bt.dirname(t),u=bt.join(r,e);return Pt(u,((t,o)=>t?n(t):o?n(null,{toCwd:u,toDst:e}):Bt.lstat(e,(t=>t?(t.message=t.message.replace("lstat","ensureSymlink"),n(t)):n(null,{toCwd:e,toDst:bt.relative(r,e)})))))}},symlinkPathsSync:function(e,t){let n;if(bt.isAbsolute(e)){if(n=Bt.existsSync(e),!n)throw new Error("absolute srcpath does not exist");return{toCwd:e,toDst:e}}{const r=bt.dirname(t),u=bt.join(r,e);if(n=Bt.existsSync(u),n)return{toCwd:u,toDst:e};if(n=Bt.existsSync(e),!n)throw new Error("relative srcpath does not exist");return{toCwd:e,toDst:bt.relative(r,e)}}}};const kt=oe;var xt={symlinkType:function(e,t,n){if(n="function"==typeof t?t:n,t="function"!=typeof t&&t)return n(null,t);kt.lstat(e,((e,r)=>{if(e)return n(null,"file");t=r&&r.isDirectory()?"dir":"file",n(null,t)}))},symlinkTypeSync:function(e,t){let n;if(t)return t;try{n=kt.lstatSync(e)}catch{return"file"}return n&&n.isDirectory()?"dir":"file"}};const Nt=M.fromCallback,Rt=e,Tt=T,Mt=Fe.mkdirs,Lt=Fe.mkdirsSync,jt=It.symlinkPaths,$t=It.symlinkPathsSync,Ht=xt.symlinkType,Gt=xt.symlinkTypeSync,Vt=ve.pathExists,{areIdentical:Ut}=Ne;function Jt(e,t,n,r){jt(e,t,((u,o)=>{if(u)return r(u);e=o.toDst,Ht(o.toCwd,n,((n,u)=>{if(n)return r(n);const o=Rt.dirname(t);Vt(o,((n,i)=>n?r(n):i?Tt.symlink(e,t,u,r):void Mt(o,(n=>{if(n)return r(n);Tt.symlink(e,t,u,r)}))))}))}))}var Wt={createSymlink:Nt((function(e,t,n,r){r="function"==typeof n?n:r,n="function"!=typeof n&&n,Tt.lstat(t,((u,o)=>{!u&&o.isSymbolicLink()?Promise.all([Tt.stat(e),Tt.stat(t)]).then((([u,o])=>{if(Ut(u,o))return r(null);Jt(e,t,n,r)})):Jt(e,t,n,r)}))})),createSymlinkSync:function(e,t,n){let r;try{r=Tt.lstatSync(t)}catch{}if(r&&r.isSymbolicLink()){const n=Tt.statSync(e),r=Tt.statSync(t);if(Ut(n,r))return}const u=$t(e,t);e=u.toDst,n=Gt(u.toCwd,n);const o=Rt.dirname(t);return Tt.existsSync(o)||Lt(o),Tt.symlinkSync(e,t,n)}};const{createFile:zt,createFileSync:Kt}=Ft,{createLink:qt,createLinkSync:Yt}=_t,{createSymlink:Zt,createSymlinkSync:Xt}=Wt;var Qt={createFile:zt,createFileSync:Kt,ensureFile:zt,ensureFileSync:Kt,createLink:qt,createLinkSync:Yt,ensureLink:qt,ensureLinkSync:Yt,createSymlink:Zt,createSymlinkSync:Xt,ensureSymlink:Zt,ensureSymlinkSync:Xt};var en={stringify:function(e,{EOL:t="\n",finalEOL:n=!0,replacer:r=null,spaces:u}={}){const o=n?t:"";return JSON.stringify(e,r,u).replace(/\n/g,t)+o},stripBom:function(e){return Buffer.isBuffer(e)&&(e=e.toString("utf8")),e.replace(/^\uFEFF/,"")}};let tn;try{tn=oe}catch(e){tn=n}const nn=M,{stringify:rn,stripBom:un}=en;const on=nn.fromPromise((async function(e,t={}){"string"==typeof t&&(t={encoding:t});const n=t.fs||tn,r=!("throws"in t)||t.throws;let u,o=await nn.fromCallback(n.readFile)(e,t);o=un(o);try{u=JSON.parse(o,t?t.reviver:null)}catch(t){if(r)throw t.message=`${e}: ${t.message}`,t;return null}return u}));const sn=nn.fromPromise((async function(e,t,n={}){const r=n.fs||tn,u=rn(t,n);await nn.fromCallback(r.writeFile)(e,u,n)}));const cn={readFile:on,readFileSync:function(e,t={}){"string"==typeof t&&(t={encoding:t});const n=t.fs||tn,r=!("throws"in t)||t.throws;try{let r=n.readFileSync(e,t);return r=un(r),JSON.parse(r,t.reviver)}catch(t){if(r)throw t.message=`${e}: ${t.message}`,t;return null}},writeFile:sn,writeFileSync:function(e,t,n={}){const r=n.fs||tn,u=rn(t,n);return r.writeFileSync(e,u,n)}};var an={readJson:cn.readFile,readJsonSync:cn.readFileSync,writeJson:cn.writeFile,writeJsonSync:cn.writeFileSync};const ln=M.fromCallback,fn=oe,dn=e,Dn=Fe,pn=ve.pathExists;var hn={outputFile:ln((function(e,t,n,r){"function"==typeof n&&(r=n,n="utf8");const u=dn.dirname(e);pn(u,((o,i)=>o?r(o):i?fn.writeFile(e,t,n,r):void Dn.mkdirs(u,(u=>{if(u)return r(u);fn.writeFile(e,t,n,r)}))))})),outputFileSync:function(e,...t){const n=dn.dirname(e);if(fn.existsSync(n))return fn.writeFileSync(e,...t);Dn.mkdirsSync(n),fn.writeFileSync(e,...t)}};const{stringify:En}=en,{outputFile:mn}=hn;var yn=async function(e,t,n={}){const r=En(t,n);await mn(e,r,n)};const{stringify:Cn}=en,{outputFileSync:Fn}=hn;var gn=function(e,t,n){const r=Cn(t,n);Fn(e,r,n)};const An=M.fromPromise,vn=an;vn.outputJson=An(yn),vn.outputJsonSync=gn,vn.outputJSON=vn.outputJson,vn.outputJSONSync=vn.outputJsonSync,vn.writeJSON=vn.writeJson,vn.writeJSONSync=vn.writeJsonSync,vn.readJSON=vn.readJson,vn.readJSONSync=vn.readJsonSync;var Sn=vn;const wn=oe,On=e,_n=ot.copy,bn=st.remove,Bn=Fe.mkdirp,Pn=ve.pathExists,In=Ne;function kn(e,t,n,r,u){return r?xn(e,t,n,u):n?bn(t,(r=>r?u(r):xn(e,t,n,u))):void Pn(t,((r,o)=>r?u(r):o?u(new Error("dest already exists.")):xn(e,t,n,u)))}function xn(e,t,n,r){wn.rename(e,t,(u=>u?"EXDEV"!==u.code?r(u):function(e,t,n,r){const u={overwrite:n,errorOnExist:!0};_n(e,t,u,(t=>t?r(t):bn(e,r)))}(e,t,n,r):r()))}var Nn=function(e,t,n,r){"function"==typeof n&&(r=n,n={});const u=(n=n||{}).overwrite||n.clobber||!1;In.checkPaths(e,t,"move",n,((n,o)=>{if(n)return r(n);const{srcStat:i,isChangingCase:s=!1}=o;In.checkParentPaths(e,i,t,"move",(n=>n?r(n):function(e){const t=On.dirname(e);return On.parse(t).root===t}(t)?kn(e,t,u,s,r):void Bn(On.dirname(t),(n=>n?r(n):kn(e,t,u,s,r)))))}))};const Rn=oe,Tn=e,Mn=ot.copySync,Ln=st.removeSync,jn=Fe.mkdirpSync,$n=Ne;function Hn(e,t,n){try{Rn.renameSync(e,t)}catch(r){if("EXDEV"!==r.code)throw r;return function(e,t,n){const r={overwrite:n,errorOnExist:!0};return Mn(e,t,r),Ln(e)}(e,t,n)}}var Gn=function(e,t,n){const r=(n=n||{}).overwrite||n.clobber||!1,{srcStat:u,isChangingCase:o=!1}=$n.checkPathsSync(e,t,"move",n);return $n.checkParentPathsSync(e,u,t,"move"),function(e){const t=Tn.dirname(e);return Tn.parse(t).root===t}(t)||jn(Tn.dirname(t)),function(e,t,n,r){if(r)return Hn(e,t,n);if(n)return Ln(t),Hn(e,t,n);if(Rn.existsSync(t))throw new Error("dest already exists.");return Hn(e,t,n)}(e,t,r,o)};var Vn,Un,Jn,Wn,zn,Kn={move:(0,M.fromCallback)(Nn),moveSync:Gn},qn={...T,...ot,...ht,...Qt,...Sn,...Fe,...Kn,...hn,...ve,...st},Yn={},Zn={exports:{}},Xn={exports:{}};function Qn(){if(Un)return Vn;Un=1;var e=1e3,t=60*e,n=60*t,r=24*n,u=7*r,o=365.25*r;function i(e,t,n,r){var u=t>=1.5*n;return Math.round(e/n)+" "+r+(u?"s":"")}return Vn=function(s,c){c=c||{};var a=typeof s;if("string"===a&&s.length>0)return function(i){if((i=String(i)).length>100)return;var s=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(i);if(!s)return;var c=parseFloat(s[1]);switch((s[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return c*o;case"weeks":case"week":case"w":return c*u;case"days":case"day":case"d":return c*r;case"hours":case"hour":case"hrs":case"hr":case"h":return c*n;case"minutes":case"minute":case"mins":case"min":case"m":return c*t;case"seconds":case"second":case"secs":case"sec":case"s":return c*e;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return c;default:return}}(s);if("number"===a&&isFinite(s))return c.long?function(u){var o=Math.abs(u);if(o>=r)return i(u,o,r,"day");if(o>=n)return i(u,o,n,"hour");if(o>=t)return i(u,o,t,"minute");if(o>=e)return i(u,o,e,"second");return u+" ms"}(s):function(u){var o=Math.abs(u);if(o>=r)return Math.round(u/r)+"d";if(o>=n)return Math.round(u/n)+"h";if(o>=t)return Math.round(u/t)+"m";if(o>=e)return Math.round(u/e)+"s";return u+"ms"}(s);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(s))}}function er(){if(Wn)return Jn;return Wn=1,Jn=function(e){function t(e){let r,u,o,i=null;function s(...e){if(!s.enabled)return;const n=s,u=Number(new Date),o=u-(r||u);n.diff=o,n.prev=r,n.curr=u,r=u,e[0]=t.coerce(e[0]),"string"!=typeof e[0]&&e.unshift("%O");let i=0;e[0]=e[0].replace(/%([a-zA-Z%])/g,((r,u)=>{if("%%"===r)return"%";i++;const o=t.formatters[u];if("function"==typeof o){const t=e[i];r=o.call(n,t),e.splice(i,1),i--}return r})),t.formatArgs.call(n,e);(n.log||t.log).apply(n,e)}return s.namespace=e,s.useColors=t.useColors(),s.color=t.selectColor(e),s.extend=n,s.destroy=t.destroy,Object.defineProperty(s,"enabled",{enumerable:!0,configurable:!1,get:()=>null!==i?i:(u!==t.namespaces&&(u=t.namespaces,o=t.enabled(e)),o),set:e=>{i=e}}),"function"==typeof t.init&&t.init(s),s}function n(e,n){const r=t(this.namespace+(void 0===n?":":n)+e);return r.log=this.log,r}function r(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return t.debug=t,t.default=t,t.coerce=function(e){if(e instanceof Error)return e.stack||e.message;return e},t.disable=function(){const e=[...t.names.map(r),...t.skips.map(r).map((e=>"-"+e))].join(",");return t.enable(""),e},t.enable=function(e){let n;t.save(e),t.namespaces=e,t.names=[],t.skips=[];const r=("string"==typeof e?e:"").split(/[\s,]+/),u=r.length;for(n=0;n{t[n]=e[n]})),t.names=[],t.skips=[],t.formatters={},t.selectColor=function(e){let n=0;for(let t=0;t{t=t||process.argv;const n=e.startsWith("-")?"":1===e.length?"-":"--",r=t.indexOf(n+e),u=t.indexOf("--");return-1!==r&&(-1===u||r=8&&Number(t[0])>=10&&Number(t[2])>=10586?Number(t[2])>=14931?3:2:1}if("CI"in r)return["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI"].some((e=>e in r))||"codeship"===r.CI_NAME?1:o;if("TEAMCITY_VERSION"in r)return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(r.TEAMCITY_VERSION)?1:0;if("truecolor"===r.COLORTERM)return 3;if("TERM_PROGRAM"in r){const e=parseInt((r.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch(r.TERM_PROGRAM){case"iTerm.app":return e>=3?3:2;case"Apple_Terminal":return 2}}return/-256(color)?$/i.test(r.TERM)?2:/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(r.TERM)||"COLORTERM"in r?1:(r.TERM,o)}(t);return function(e){return 0!==e&&{level:e,hasBasic:!0,has256:e>=2,has16m:e>=3}}(o)}return n("no-color")||n("no-colors")||n("color=false")?u=!1:(n("color")||n("colors")||n("color=true")||n("color=always"))&&(u=!0),"FORCE_COLOR"in r&&(u=0===r.FORCE_COLOR.length||0!==parseInt(r.FORCE_COLOR,10)),rr={supportsColor:o,stdout:o(process.stdout),stderr:o(process.stderr)}}function lr(){return or||(or=1,function(e,t){const n=a,r=i;t.init=function(e){e.inspectOpts={};const n=Object.keys(t.inspectOpts);for(let r=0;r{}),"Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."),t.colors=[6,2,3,4,5,1];try{const e=ar();e&&(e.stderr||e).level>=2&&(t.colors=[20,21,26,27,32,33,38,39,40,41,42,43,44,45,56,57,62,63,68,69,74,75,76,77,78,79,80,81,92,93,98,99,112,113,128,129,134,135,148,149,160,161,162,163,164,165,166,167,168,169,170,171,172,173,178,179,184,185,196,197,198,199,200,201,202,203,204,205,206,207,208,209,214,215,220,221])}catch(e){}t.inspectOpts=Object.keys(process.env).filter((e=>/^debug_/i.test(e))).reduce(((e,t)=>{const n=t.substring(6).toLowerCase().replace(/_([a-z])/g,((e,t)=>t.toUpperCase()));let r=process.env[t];return r=!!/^(yes|on|true|enabled)$/i.test(r)||!/^(no|off|false|disabled)$/i.test(r)&&("null"===r?null:Number(r)),e[n]=r,e}),{}),e.exports=er()(t);const{formatters:u}=e.exports;u.o=function(e){return this.inspectOpts.colors=this.useColors,r.inspect(e,this.inspectOpts).split("\n").map((e=>e.trim())).join(" ")},u.O=function(e){return this.inspectOpts.colors=this.useColors,r.inspect(e,this.inspectOpts)}}(sr,sr.exports)),sr.exports}ir=Zn,"undefined"==typeof process||"renderer"===process.type||!0===process.browser||process.__nwjs?ir.exports=(zn||(zn=1,function(e,t){t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const n="color: "+this.color;t.splice(1,0,n,"color: inherit");let r=0,u=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{"%%"!==e&&(r++,"%c"===e&&(u=r))})),t.splice(u,0,n)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}return!e&&"undefined"!=typeof process&&"env"in process&&(e=process.env.DEBUG),e},t.useColors=function(){return!("undefined"==typeof window||!window.process||"renderer"!==window.process.type&&!window.process.__nwjs)||("undefined"==typeof navigator||!navigator.userAgent||!navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))&&("undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))},t.storage=function(){try{return localStorage}catch(e){}}(),t.destroy=(()=>{let e=!1;return()=>{e||(e=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],t.log=console.debug||console.log||(()=>{}),e.exports=er()(t);const{formatters:n}=e.exports;n.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}}(Xn,Xn.exports)),Xn.exports):ir.exports=lr();var fr=function(e){return(e=e||{}).circles?function(e){var t=[],n=[];return e.proto?function e(u){if("object"!=typeof u||null===u)return u;if(u instanceof Date)return new Date(u);if(Array.isArray(u))return r(u,e);if(u instanceof Map)return new Map(r(Array.from(u),e));if(u instanceof Set)return new Set(r(Array.from(u),e));var o={};for(var i in t.push(u),n.push(o),u){var s=u[i];if("object"!=typeof s||null===s)o[i]=s;else if(s instanceof Date)o[i]=new Date(s);else if(s instanceof Map)o[i]=new Map(r(Array.from(s),e));else if(s instanceof Set)o[i]=new Set(r(Array.from(s),e));else if(ArrayBuffer.isView(s))o[i]=dr(s);else{var c=t.indexOf(s);o[i]=-1!==c?n[c]:e(s)}}return t.pop(),n.pop(),o}:function e(u){if("object"!=typeof u||null===u)return u;if(u instanceof Date)return new Date(u);if(Array.isArray(u))return r(u,e);if(u instanceof Map)return new Map(r(Array.from(u),e));if(u instanceof Set)return new Set(r(Array.from(u),e));var o={};for(var i in t.push(u),n.push(o),u)if(!1!==Object.hasOwnProperty.call(u,i)){var s=u[i];if("object"!=typeof s||null===s)o[i]=s;else if(s instanceof Date)o[i]=new Date(s);else if(s instanceof Map)o[i]=new Map(r(Array.from(s),e));else if(s instanceof Set)o[i]=new Set(r(Array.from(s),e));else if(ArrayBuffer.isView(s))o[i]=dr(s);else{var c=t.indexOf(s);o[i]=-1!==c?n[c]:e(s)}}return t.pop(),n.pop(),o};function r(e,r){for(var u=Object.keys(e),o=new Array(u.length),i=0;i!e,yr=e=>e&&"object"==typeof e&&!Array.isArray(e),Cr=(e,t,n)=>{(Array.isArray(t)?t:[t]).forEach((t=>{if(t)throw new Error(`Problem with log4js configuration: (${Dr.inspect(e,{depth:5})}) - ${n}`)}))};var Fr={configure:e=>{pr("New configuration to be validated: ",e),Cr(e,mr(yr(e)),"must be an object."),pr(`Calling pre-processing listeners (${hr.length})`),hr.forEach((t=>t(e))),pr("Configuration pre-processing finished."),pr(`Calling configuration listeners (${Er.length})`),Er.forEach((t=>t(e))),pr("Configuration finished.")},addListener:e=>{Er.push(e),pr(`Added listener, now ${Er.length} listeners`)},addPreProcessingListener:e=>{hr.push(e),pr(`Added pre-processing listener, now ${hr.length} listeners`)},throwExceptionIf:Cr,anObject:yr,anInteger:e=>e&&"number"==typeof e&&Number.isInteger(e),validIdentifier:e=>/^[A-Za-z][A-Za-z0-9_]*$/g.test(e),not:mr},gr={exports:{}};!function(e){function t(e,t){for(var n=e.toString();n.length-1?s:c,l=n(u.getHours()),f=n(u.getMinutes()),d=n(u.getSeconds()),D=t(u.getMilliseconds(),3),p=function(e){var t=Math.abs(e),n=String(Math.floor(t/60)),r=String(t%60);return n=("0"+n).slice(-2),r=("0"+r).slice(-2),0===e?"Z":(e<0?"+":"-")+n+":"+r}(u.getTimezoneOffset());return r.replace(/dd/g,o).replace(/MM/g,i).replace(/y{1,4}/g,a).replace(/hh/g,l).replace(/mm/g,f).replace(/ss/g,d).replace(/SSS/g,D).replace(/O/g,p)}function u(e,t,n,r){e["set"+(r?"":"UTC")+t](n)}e.exports=r,e.exports.asString=r,e.exports.parse=function(t,n,r){if(!t)throw new Error("pattern must be supplied");return function(t,n,r){var o=t.indexOf("O")<0,i=!1,s=[{pattern:/y{1,4}/,regexp:"\\d{1,4}",fn:function(e,t){u(e,"FullYear",t,o)}},{pattern:/MM/,regexp:"\\d{1,2}",fn:function(e,t){u(e,"Month",t-1,o),e.getMonth()!==t-1&&(i=!0)}},{pattern:/dd/,regexp:"\\d{1,2}",fn:function(e,t){i&&u(e,"Month",e.getMonth()-1,o),u(e,"Date",t,o)}},{pattern:/hh/,regexp:"\\d{1,2}",fn:function(e,t){u(e,"Hours",t,o)}},{pattern:/mm/,regexp:"\\d\\d",fn:function(e,t){u(e,"Minutes",t,o)}},{pattern:/ss/,regexp:"\\d\\d",fn:function(e,t){u(e,"Seconds",t,o)}},{pattern:/SSS/,regexp:"\\d\\d\\d",fn:function(e,t){u(e,"Milliseconds",t,o)}},{pattern:/O/,regexp:"[+-]\\d{1,2}:?\\d{2}?|Z",fn:function(e,t){t="Z"===t?0:t.replace(":","");var n=Math.abs(t),r=(t>0?-1:1)*(n%100+60*Math.floor(n/100));e.setUTCMinutes(e.getUTCMinutes()+r)}}],c=s.reduce((function(e,t){return t.pattern.test(e.regexp)?(t.index=e.regexp.match(t.pattern).index,e.regexp=e.regexp.replace(t.pattern,"("+t.regexp+")")):t.index=-1,e}),{regexp:t,index:[]}),a=s.filter((function(e){return e.index>-1}));a.sort((function(e,t){return e.index-t.index}));var l=new RegExp(c.regexp).exec(n);if(l){var f=r||e.exports.now();return a.forEach((function(e,t){e.fn(f,l[t+1])})),f}throw new Error("String '"+n+"' could not be parsed as '"+t+"'")}(t,n,r)},e.exports.now=function(){return new Date},e.exports.ISO8601_FORMAT="yyyy-MM-ddThh:mm:ss.SSS",e.exports.ISO8601_WITH_TZ_OFFSET_FORMAT="yyyy-MM-ddThh:mm:ss.SSSO",e.exports.DATETIME_FORMAT="dd MM yyyy hh:mm:ss.SSS",e.exports.ABSOLUTETIME_FORMAT="hh:mm:ss.SSS"}(gr);const Ar=gr.exports,vr=t,Sr=i,wr=e,Or=l,_r=Zn.exports("log4js:layouts"),br={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[90,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[91,39],yellow:[33,39]};function Br(e){return e?`[${br[e][0]}m`:""}function Pr(e){return e?`[${br[e][1]}m`:""}function Ir(e,t){return n=Sr.format("[%s] [%s] %s - ",Ar.asString(e.startTime),e.level.toString(),e.categoryName),Br(r=t)+n+Pr(r);var n,r}function kr(e){return Ir(e)+Sr.format(...e.data)}function xr(e){return Ir(e,e.level.colour)+Sr.format(...e.data)}function Nr(e){return Sr.format(...e.data)}function Rr(e){return e.data[0]}function Tr(e,t){const n=/%(-?[0-9]+)?(\.?-?[0-9]+)?([[\]cdhmnprzxXyflosCMAF%])(\{([^}]+)\})?|([^%]+)/;function r(e){return e&&e.pid?e.pid.toString():process.pid.toString()}e=e||"%r %p %c - %m%n";const u={c:function(e,t){let n=e.categoryName;if(t){const e=parseInt(t,10),r=n.split(".");ee&&(n=r.slice(-e).join(wr.sep))}return n},l:function(e){return e.lineNumber?`${e.lineNumber}`:""},o:function(e){return e.columnNumber?`${e.columnNumber}`:""},s:function(e){return e.callStack||""},C:function(e){return e.className||""},M:function(e){return e.functionName||""},A:function(e){return e.functionAlias||""},F:function(e){return e.callerName||""}};function o(e,t,n){return u[e](t,n)}function i(e,t,n){let r=e;return r=function(e,t){let n;return e?(n=parseInt(e.slice(1),10),n>0?t.slice(0,n):t.slice(n)):t}(t,r),r=function(e,t){let n;if(e)if("-"===e.charAt(0))for(n=parseInt(e.slice(1),10);t.lengthNr,basic:()=>kr,colored:()=>xr,coloured:()=>xr,pattern:e=>Tr(e&&e.pattern,e&&e.tokens),dummy:()=>Rr};var Lr={basicLayout:kr,messagePassThroughLayout:Nr,patternLayout:Tr,colouredLayout:xr,coloredLayout:xr,dummyLayout:Rr,addLayout(e,t){Mr[e]=t},layout:(e,t)=>Mr[e]&&Mr[e](t)};const jr=Fr,$r=["white","grey","black","blue","cyan","green","magenta","red","yellow"];class Hr{constructor(e,t,n){this.level=e,this.levelStr=t,this.colour=n}toString(){return this.levelStr}static getLevel(e,t){return e?e instanceof Hr?e:(e instanceof Object&&e.levelStr&&(e=e.levelStr),Hr[e.toString().toUpperCase()]||t):t}static addLevels(e){if(e){Object.keys(e).forEach((t=>{const n=t.toUpperCase();Hr[n]=new Hr(e[t].value,n,e[t].colour);const r=Hr.levels.findIndex((e=>e.levelStr===n));r>-1?Hr.levels[r]=Hr[n]:Hr.levels.push(Hr[n])})),Hr.levels.sort(((e,t)=>e.level-t.level))}}isLessThanOrEqualTo(e){return"string"==typeof e&&(e=Hr.getLevel(e)),this.level<=e.level}isGreaterThanOrEqualTo(e){return"string"==typeof e&&(e=Hr.getLevel(e)),this.level>=e.level}isEqualTo(e){return"string"==typeof e&&(e=Hr.getLevel(e)),this.level===e.level}}Hr.levels=[],Hr.addLevels({ALL:{value:Number.MIN_VALUE,colour:"grey"},TRACE:{value:5e3,colour:"blue"},DEBUG:{value:1e4,colour:"cyan"},INFO:{value:2e4,colour:"green"},WARN:{value:3e4,colour:"yellow"},ERROR:{value:4e4,colour:"red"},FATAL:{value:5e4,colour:"magenta"},MARK:{value:9007199254740992,colour:"grey"},OFF:{value:Number.MAX_VALUE,colour:"grey"}}),jr.addListener((e=>{const t=e.levels;if(t){jr.throwExceptionIf(e,jr.not(jr.anObject(t)),"levels must be an object");Object.keys(t).forEach((n=>{jr.throwExceptionIf(e,jr.not(jr.validIdentifier(n)),`level name "${n}" is not a valid identifier (must start with a letter, only contain A-Z,a-z,0-9,_)`),jr.throwExceptionIf(e,jr.not(jr.anObject(t[n])),`level "${n}" must be an object`),jr.throwExceptionIf(e,jr.not(t[n].value),`level "${n}" must have a 'value' property`),jr.throwExceptionIf(e,jr.not(jr.anInteger(t[n].value)),`level "${n}".value must have an integer value`),jr.throwExceptionIf(e,jr.not(t[n].colour),`level "${n}" must have a 'colour' property`),jr.throwExceptionIf(e,jr.not($r.indexOf(t[n].colour)>-1),`level "${n}".colour must be one of ${$r.join(", ")}`)}))}})),jr.addListener((e=>{Hr.addLevels(e.levels)}));var Gr=Hr,Vr={exports:{}},Ur={};const{parse:Jr,stringify:Wr}=JSON,{keys:zr}=Object,Kr=String,qr="string",Yr={},Zr="object",Xr=(e,t)=>t,Qr=e=>e instanceof Kr?Kr(e):e,eu=(e,t)=>typeof t===qr?new Kr(t):t,tu=(e,t,n,r)=>{const u=[];for(let o=zr(n),{length:i}=o,s=0;s{const r=Kr(t.push(n)-1);return e.set(n,r),r},ru=(e,t)=>{const n=Jr(e,eu).map(Qr),r=n[0],u=t||Xr,o=typeof r===Zr&&r?tu(n,new Set,r,u):r;return u.call({"":o},"",o)};Ur.parse=ru;const uu=(e,t,n)=>{const r=t&&typeof t===Zr?(e,n)=>""===e||-1Jr(uu(e));Ur.fromJSON=e=>ru(Wr(e));const ou=Ur,iu=Gr;const su=new class{constructor(){const e={__LOG4JS_undefined__:void 0,__LOG4JS_NaN__:Number("abc"),__LOG4JS_Infinity__:1/0,"__LOG4JS_-Infinity__":-1/0};this.deMap=e,this.serMap={},Object.keys(this.deMap).forEach((e=>{const t=this.deMap[e];this.serMap[t]=e}))}canSerialise(e){return"string"!=typeof e&&e in this.serMap}serialise(e){return this.canSerialise(e)?this.serMap[e]:e}canDeserialise(e){return e in this.deMap}deserialise(e){return this.canDeserialise(e)?this.deMap[e]:e}};let cu=class{constructor(e,t,n,r,u,o){if(this.startTime=new Date,this.categoryName=e,this.data=n,this.level=t,this.context=Object.assign({},r),this.pid=process.pid,this.error=o,void 0!==u){if(!u||"object"!=typeof u||Array.isArray(u))throw new TypeError("Invalid location type passed to LoggingEvent constructor");this.constructor._getLocationKeys().forEach((e=>{void 0!==u[e]&&(this[e]=u[e])}))}}static _getLocationKeys(){return["fileName","lineNumber","columnNumber","callStack","className","functionName","functionAlias","callerName"]}serialise(){return ou.stringify(this,((e,t)=>(t instanceof Error&&(t=Object.assign({message:t.message,stack:t.stack},t)),su.serialise(t))))}static deserialise(e){let t;try{const n=ou.parse(e,((e,t)=>{if(t&&t.message&&t.stack){const e=new Error(t);Object.keys(t).forEach((n=>{e[n]=t[n]})),t=e}return su.deserialise(t)}));this._getLocationKeys().forEach((e=>{void 0!==n[e]&&(n.location||(n.location={}),n.location[e]=n[e])})),t=new cu(n.categoryName,iu.getLevel(n.level.levelStr),n.data,n.context,n.location,n.error),t.startTime=new Date(n.startTime),t.pid=n.pid,n.cluster&&(t.cluster=n.cluster)}catch(n){t=new cu("log4js",iu.ERROR,["Unable to parse log:",e,"because: ",n])}return t}};var au=cu;const lu=Zn.exports("log4js:clustering"),fu=au,du=Fr;let Du=!1,pu=null;try{pu=require("cluster")}catch(e){lu("cluster module not present"),Du=!0}const hu=[];let Eu=!1,mu="NODE_APP_INSTANCE";const yu=()=>Eu&&"0"===process.env[mu],Cu=()=>Du||pu&&pu.isMaster||yu(),Fu=e=>{hu.forEach((t=>t(e)))},gu=(e,t)=>{if(lu("cluster message received from worker ",e,": ",t),e.topic&&e.data&&(t=e,e=void 0),t&&t.topic&&"log4js:message"===t.topic){lu("received message: ",t.data);const e=fu.deserialise(t.data);Fu(e)}};Du||du.addListener((e=>{hu.length=0,({pm2:Eu,disableClustering:Du,pm2InstanceVar:mu="NODE_APP_INSTANCE"}=e),lu(`clustering disabled ? ${Du}`),lu(`cluster.isMaster ? ${pu&&pu.isMaster}`),lu(`pm2 enabled ? ${Eu}`),lu(`pm2InstanceVar = ${mu}`),lu(`process.env[${mu}] = ${process.env[mu]}`),Eu&&process.removeListener("message",gu),pu&&pu.removeListener&&pu.removeListener("message",gu),Du||e.disableClustering?lu("Not listening for cluster messages, because clustering disabled."):yu()?(lu("listening for PM2 broadcast messages"),process.on("message",gu)):pu&&pu.isMaster?(lu("listening for cluster messages"),pu.on("message",gu)):lu("not listening for messages, because we are not a master process")}));var Au={onlyOnMaster:(e,t)=>Cu()?e():t,isMaster:Cu,send:e=>{Cu()?Fu(e):(Eu||(e.cluster={workerId:pu.worker.id,worker:process.pid}),process.send({topic:"log4js:message",data:e.serialise()}))},onMessage:e=>{hu.push(e)}},vu={};function Su(e){if("number"==typeof e&&Number.isInteger(e))return e;const t={K:1024,M:1048576,G:1073741824},n=Object.keys(t),r=e.slice(-1).toLocaleUpperCase(),u=e.slice(0,-1).trim();if(n.indexOf(r)<0||!Number.isInteger(Number(u)))throw Error(`maxLogSize: "${e}" is invalid`);return u*t[r]}function wu(e){return function(e,t){const n=Object.assign({},t);return Object.keys(e).forEach((r=>{n[r]&&(n[r]=e[r](t[r]))})),n}({maxLogSize:Su},e)}const Ou={dateFile:wu,file:wu,fileSync:wu};vu.modifyConfig=e=>Ou[e.type]?Ou[e.type](e):e;var _u={};const bu=console.log.bind(console);_u.configure=function(e,t){let n=t.colouredLayout;return e.layout&&(n=t.layout(e.layout.type,e.layout)),function(e,t){return n=>{bu(e(n,t))}}(n,e.timezoneOffset)};var Bu={};Bu.configure=function(e,t){let n=t.colouredLayout;return e.layout&&(n=t.layout(e.layout.type,e.layout)),function(e,t){return n=>{process.stdout.write(`${e(n,t)}\n`)}}(n,e.timezoneOffset)};var Pu={};Pu.configure=function(e,t){let n=t.colouredLayout;return e.layout&&(n=t.layout(e.layout.type,e.layout)),function(e,t){return n=>{process.stderr.write(`${e(n,t)}\n`)}}(n,e.timezoneOffset)};var Iu={};Iu.configure=function(e,t,n,r){const u=n(e.appender);return function(e,t,n,r){const u=r.getLevel(e),o=r.getLevel(t,r.FATAL);return e=>{const t=e.level;u.isLessThanOrEqualTo(t)&&o.isGreaterThanOrEqualTo(t)&&n(e)}}(e.level,e.maxLevel,u,r)};var ku={};const xu=Zn.exports("log4js:categoryFilter");ku.configure=function(e,t,n){const r=n(e.appender);return function(e,t){return"string"==typeof e&&(e=[e]),n=>{xu(`Checking ${n.categoryName} against ${e}`),-1===e.indexOf(n.categoryName)&&(xu("Not excluded, sending to appender"),t(n))}}(e.exclude,r)};var Nu={};const Ru=Zn.exports("log4js:noLogFilter");Nu.configure=function(e,t,n){const r=n(e.appender);return function(e,t){return n=>{Ru(`Checking data: ${n.data} against filters: ${e}`),"string"==typeof e&&(e=[e]),e=e.filter((e=>null!=e&&""!==e));const r=new RegExp(e.join("|"),"i");(0===e.length||n.data.findIndex((e=>r.test(e)))<0)&&(Ru("Not excluded, sending to appender"),t(n))}}(e.exclude,r)};var Tu={},Mu={exports:{}},Lu={},ju={fromCallback:function(e){return Object.defineProperty((function(){if("function"!=typeof arguments[arguments.length-1])return new Promise(((t,n)=>{arguments[arguments.length]=(e,r)=>{if(e)return n(e);t(r)},arguments.length++,e.apply(this,arguments)}));e.apply(this,arguments)}),"name",{value:e.name})},fromPromise:function(e){return Object.defineProperty((function(){const t=arguments[arguments.length-1];if("function"!=typeof t)return e.apply(this,arguments);e.apply(this,arguments).then((e=>t(null,e)),t)}),"name",{value:e.name})}};!function(e){const t=ju.fromCallback,n=oe,r=["access","appendFile","chmod","chown","close","copyFile","fchmod","fchown","fdatasync","fstat","fsync","ftruncate","futimes","lchown","lchmod","link","lstat","mkdir","mkdtemp","open","readFile","readdir","readlink","realpath","rename","rmdir","stat","symlink","truncate","unlink","utimes","writeFile"].filter((e=>"function"==typeof n[e]));Object.keys(n).forEach((t=>{"promises"!==t&&(e[t]=n[t])})),r.forEach((r=>{e[r]=t(n[r])})),e.exists=function(e,t){return"function"==typeof t?n.exists(e,t):new Promise((t=>n.exists(e,t)))},e.read=function(e,t,r,u,o,i){return"function"==typeof i?n.read(e,t,r,u,o,i):new Promise(((i,s)=>{n.read(e,t,r,u,o,((e,t,n)=>{if(e)return s(e);i({bytesRead:t,buffer:n})}))}))},e.write=function(e,t,...r){return"function"==typeof r[r.length-1]?n.write(e,t,...r):new Promise(((u,o)=>{n.write(e,t,...r,((e,t,n)=>{if(e)return o(e);u({bytesWritten:t,buffer:n})}))}))},"function"==typeof n.realpath.native&&(e.realpath.native=t(n.realpath.native))}(Lu);const $u=e;function Hu(e){return(e=$u.normalize($u.resolve(e)).split($u.sep)).length>0?e[0]:null}const Gu=/[<>:"|?*]/;var Vu=function(e){const t=Hu(e);return e=e.replace(t,""),Gu.test(e)};const Uu=oe,Ju=e,Wu=Vu,zu=parseInt("0777",8);var Ku=function e(t,n,r,u){if("function"==typeof n?(r=n,n={}):n&&"object"==typeof n||(n={mode:n}),"win32"===process.platform&&Wu(t)){const e=new Error(t+" contains invalid WIN32 path characters.");return e.code="EINVAL",r(e)}let o=n.mode;const i=n.fs||Uu;void 0===o&&(o=zu&~process.umask()),u||(u=null),r=r||function(){},t=Ju.resolve(t),i.mkdir(t,o,(o=>{if(!o)return r(null,u=u||t);if("ENOENT"===o.code){if(Ju.dirname(t)===t)return r(o);e(Ju.dirname(t),n,((u,o)=>{u?r(u,o):e(t,n,r,o)}))}else i.stat(t,((e,t)=>{e||!t.isDirectory()?r(o,u):r(null,u)}))}))};const qu=oe,Yu=e,Zu=Vu,Xu=parseInt("0777",8);var Qu=function e(t,n,r){n&&"object"==typeof n||(n={mode:n});let u=n.mode;const o=n.fs||qu;if("win32"===process.platform&&Zu(t)){const e=new Error(t+" contains invalid WIN32 path characters.");throw e.code="EINVAL",e}void 0===u&&(u=Xu&~process.umask()),r||(r=null),t=Yu.resolve(t);try{o.mkdirSync(t,u),r=r||t}catch(u){if("ENOENT"===u.code){if(Yu.dirname(t)===t)throw u;r=e(Yu.dirname(t),n,r),e(t,n,r)}else{let e;try{e=o.statSync(t)}catch(e){throw u}if(!e.isDirectory())throw u}}return r};const eo=(0,ju.fromCallback)(Ku);var to={mkdirs:eo,mkdirsSync:Qu,mkdirp:eo,mkdirpSync:Qu,ensureDir:eo,ensureDirSync:Qu};const no=oe;var ro=function(e,t,n,r){no.open(e,"r+",((e,u)=>{if(e)return r(e);no.futimes(u,t,n,(e=>{no.close(u,(t=>{r&&r(e||t)}))}))}))},uo=function(e,t,n){const r=no.openSync(e,"r+");return no.futimesSync(r,t,n),no.closeSync(r)};const oo=oe,io=e,so=10,co=5,ao=0,lo=process.versions.node.split("."),fo=Number.parseInt(lo[0],10),Do=Number.parseInt(lo[1],10),po=Number.parseInt(lo[2],10);function ho(){if(fo>so)return!0;if(fo===so){if(Do>co)return!0;if(Do===co&&po>=ao)return!0}return!1}function Eo(e,t){const n=io.resolve(e).split(io.sep).filter((e=>e)),r=io.resolve(t).split(io.sep).filter((e=>e));return n.reduce(((e,t,n)=>e&&r[n]===t),!0)}function mo(e,t,n){return`Cannot ${n} '${e}' to a subdirectory of itself, '${t}'.`}var yo,Co,Fo={checkPaths:function(e,t,n,r){!function(e,t,n){ho()?oo.stat(e,{bigint:!0},((e,r)=>{if(e)return n(e);oo.stat(t,{bigint:!0},((e,t)=>e?"ENOENT"===e.code?n(null,{srcStat:r,destStat:null}):n(e):n(null,{srcStat:r,destStat:t})))})):oo.stat(e,((e,r)=>{if(e)return n(e);oo.stat(t,((e,t)=>e?"ENOENT"===e.code?n(null,{srcStat:r,destStat:null}):n(e):n(null,{srcStat:r,destStat:t})))}))}(e,t,((u,o)=>{if(u)return r(u);const{srcStat:i,destStat:s}=o;return s&&s.ino&&s.dev&&s.ino===i.ino&&s.dev===i.dev?r(new Error("Source and destination must not be the same.")):i.isDirectory()&&Eo(e,t)?r(new Error(mo(e,t,n))):r(null,{srcStat:i,destStat:s})}))},checkPathsSync:function(e,t,n){const{srcStat:r,destStat:u}=function(e,t){let n,r;n=ho()?oo.statSync(e,{bigint:!0}):oo.statSync(e);try{r=ho()?oo.statSync(t,{bigint:!0}):oo.statSync(t)}catch(e){if("ENOENT"===e.code)return{srcStat:n,destStat:null};throw e}return{srcStat:n,destStat:r}}(e,t);if(u&&u.ino&&u.dev&&u.ino===r.ino&&u.dev===r.dev)throw new Error("Source and destination must not be the same.");if(r.isDirectory()&&Eo(e,t))throw new Error(mo(e,t,n));return{srcStat:r,destStat:u}},checkParentPaths:function e(t,n,r,u,o){const i=io.resolve(io.dirname(t)),s=io.resolve(io.dirname(r));if(s===i||s===io.parse(s).root)return o();ho()?oo.stat(s,{bigint:!0},((i,c)=>i?"ENOENT"===i.code?o():o(i):c.ino&&c.dev&&c.ino===n.ino&&c.dev===n.dev?o(new Error(mo(t,r,u))):e(t,n,s,u,o))):oo.stat(s,((i,c)=>i?"ENOENT"===i.code?o():o(i):c.ino&&c.dev&&c.ino===n.ino&&c.dev===n.dev?o(new Error(mo(t,r,u))):e(t,n,s,u,o)))},checkParentPathsSync:function e(t,n,r,u){const o=io.resolve(io.dirname(t)),i=io.resolve(io.dirname(r));if(i===o||i===io.parse(i).root)return;let s;try{s=ho()?oo.statSync(i,{bigint:!0}):oo.statSync(i)}catch(e){if("ENOENT"===e.code)return;throw e}if(s.ino&&s.dev&&s.ino===n.ino&&s.dev===n.dev)throw new Error(mo(t,r,u));return e(t,n,i,u)},isSrcSubdir:Eo};const go=oe,Ao=e,vo=to.mkdirsSync,So=uo,wo=Fo;function Oo(e,t,n,r){if(!r.filter||r.filter(t,n))return function(e,t,n,r){const u=r.dereference?go.statSync:go.lstatSync,o=u(t);if(o.isDirectory())return function(e,t,n,r,u){if(!t)return function(e,t,n,r){return go.mkdirSync(n),bo(t,n,r),go.chmodSync(n,e.mode)}(e,n,r,u);if(t&&!t.isDirectory())throw new Error(`Cannot overwrite non-directory '${r}' with directory '${n}'.`);return bo(n,r,u)}(o,e,t,n,r);if(o.isFile()||o.isCharacterDevice()||o.isBlockDevice())return function(e,t,n,r,u){return t?function(e,t,n,r){if(r.overwrite)return go.unlinkSync(n),_o(e,t,n,r);if(r.errorOnExist)throw new Error(`'${n}' already exists`)}(e,n,r,u):_o(e,n,r,u)}(o,e,t,n,r);if(o.isSymbolicLink())return function(e,t,n,r){let u=go.readlinkSync(t);r.dereference&&(u=Ao.resolve(process.cwd(),u));if(e){let e;try{e=go.readlinkSync(n)}catch(e){if("EINVAL"===e.code||"UNKNOWN"===e.code)return go.symlinkSync(u,n);throw e}if(r.dereference&&(e=Ao.resolve(process.cwd(),e)),wo.isSrcSubdir(u,e))throw new Error(`Cannot copy '${u}' to a subdirectory of itself, '${e}'.`);if(go.statSync(n).isDirectory()&&wo.isSrcSubdir(e,u))throw new Error(`Cannot overwrite '${e}' with '${u}'.`);return function(e,t){return go.unlinkSync(t),go.symlinkSync(e,t)}(u,n)}return go.symlinkSync(u,n)}(e,t,n,r)}(e,t,n,r)}function _o(e,t,n,r){return"function"==typeof go.copyFileSync?(go.copyFileSync(t,n),go.chmodSync(n,e.mode),r.preserveTimestamps?So(n,e.atime,e.mtime):void 0):function(e,t,n,r){const u=65536,o=(Co?yo:(Co=1,yo=function(e){if("function"==typeof Buffer.allocUnsafe)try{return Buffer.allocUnsafe(e)}catch(t){return new Buffer(e)}return new Buffer(e)}))(u),i=go.openSync(t,"r"),s=go.openSync(n,"w",e.mode);let c=0;for(;cfunction(e,t,n,r){const u=Ao.join(t,e),o=Ao.join(n,e),{destStat:i}=wo.checkPathsSync(u,o,"copy");return Oo(i,u,o,r)}(r,e,t,n)))}var Bo=function(e,t,n){"function"==typeof n&&(n={filter:n}),(n=n||{}).clobber=!("clobber"in n)||!!n.clobber,n.overwrite="overwrite"in n?!!n.overwrite:n.clobber,n.preserveTimestamps&&"ia32"===process.arch&&console.warn("fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n\n see https://github.com/jprichardson/node-fs-extra/issues/269");const{srcStat:r,destStat:u}=wo.checkPathsSync(e,t,"copy");return wo.checkParentPathsSync(e,r,t,"copy"),function(e,t,n,r){if(r.filter&&!r.filter(t,n))return;const u=Ao.dirname(n);go.existsSync(u)||vo(u);return Oo(e,t,n,r)}(u,e,t,n)},Po={copySync:Bo};const Io=ju.fromPromise,ko=Lu;var xo={pathExists:Io((function(e){return ko.access(e).then((()=>!0)).catch((()=>!1))})),pathExistsSync:ko.existsSync};const No=oe,Ro=e,To=to.mkdirs,Mo=xo.pathExists,Lo=ro,jo=Fo;function $o(e,t,n,r,u){const o=Ro.dirname(n);Mo(o,((i,s)=>i?u(i):s?Go(e,t,n,r,u):void To(o,(o=>o?u(o):Go(e,t,n,r,u)))))}function Ho(e,t,n,r,u,o){Promise.resolve(u.filter(n,r)).then((i=>i?e(t,n,r,u,o):o()),(e=>o(e)))}function Go(e,t,n,r,u){return r.filter?Ho(Vo,e,t,n,r,u):Vo(e,t,n,r,u)}function Vo(e,t,n,r,u){(r.dereference?No.stat:No.lstat)(t,((o,i)=>o?u(o):i.isDirectory()?function(e,t,n,r,u,o){if(!t)return function(e,t,n,r,u){No.mkdir(n,(o=>{if(o)return u(o);Wo(t,n,r,(t=>t?u(t):No.chmod(n,e.mode,u)))}))}(e,n,r,u,o);if(t&&!t.isDirectory())return o(new Error(`Cannot overwrite non-directory '${r}' with directory '${n}'.`));return Wo(n,r,u,o)}(i,e,t,n,r,u):i.isFile()||i.isCharacterDevice()||i.isBlockDevice()?function(e,t,n,r,u,o){return t?function(e,t,n,r,u){if(!r.overwrite)return r.errorOnExist?u(new Error(`'${n}' already exists`)):u();No.unlink(n,(o=>o?u(o):Uo(e,t,n,r,u)))}(e,n,r,u,o):Uo(e,n,r,u,o)}(i,e,t,n,r,u):i.isSymbolicLink()?function(e,t,n,r,u){No.readlink(t,((t,o)=>t?u(t):(r.dereference&&(o=Ro.resolve(process.cwd(),o)),e?void No.readlink(n,((t,i)=>t?"EINVAL"===t.code||"UNKNOWN"===t.code?No.symlink(o,n,u):u(t):(r.dereference&&(i=Ro.resolve(process.cwd(),i)),jo.isSrcSubdir(o,i)?u(new Error(`Cannot copy '${o}' to a subdirectory of itself, '${i}'.`)):e.isDirectory()&&jo.isSrcSubdir(i,o)?u(new Error(`Cannot overwrite '${i}' with '${o}'.`)):function(e,t,n){No.unlink(t,(r=>r?n(r):No.symlink(e,t,n)))}(o,n,u)))):No.symlink(o,n,u))))}(e,t,n,r,u):void 0))}function Uo(e,t,n,r,u){return"function"==typeof No.copyFile?No.copyFile(t,n,(t=>t?u(t):Jo(e,n,r,u))):function(e,t,n,r,u){const o=No.createReadStream(t);o.on("error",(e=>u(e))).once("open",(()=>{const t=No.createWriteStream(n,{mode:e.mode});t.on("error",(e=>u(e))).on("open",(()=>o.pipe(t))).once("close",(()=>Jo(e,n,r,u)))}))}(e,t,n,r,u)}function Jo(e,t,n,r){No.chmod(t,e.mode,(u=>u?r(u):n.preserveTimestamps?Lo(t,e.atime,e.mtime,r):r()))}function Wo(e,t,n,r){No.readdir(e,((u,o)=>u?r(u):zo(o,e,t,n,r)))}function zo(e,t,n,r,u){const o=e.pop();return o?function(e,t,n,r,u,o){const i=Ro.join(n,t),s=Ro.join(r,t);jo.checkPaths(i,s,"copy",((t,c)=>{if(t)return o(t);const{destStat:a}=c;Go(a,i,s,u,(t=>t?o(t):zo(e,n,r,u,o)))}))}(e,o,t,n,r,u):u()}var Ko=function(e,t,n,r){"function"!=typeof n||r?"function"==typeof n&&(n={filter:n}):(r=n,n={}),r=r||function(){},(n=n||{}).clobber=!("clobber"in n)||!!n.clobber,n.overwrite="overwrite"in n?!!n.overwrite:n.clobber,n.preserveTimestamps&&"ia32"===process.arch&&console.warn("fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n\n see https://github.com/jprichardson/node-fs-extra/issues/269"),jo.checkPaths(e,t,"copy",((u,o)=>{if(u)return r(u);const{srcStat:i,destStat:s}=o;jo.checkParentPaths(e,i,t,"copy",(u=>u?r(u):n.filter?Ho($o,s,e,t,n,r):$o(s,e,t,n,r)))}))};var qo={copy:(0,ju.fromCallback)(Ko)};const Yo=oe,Zo=e,Xo=s,Qo="win32"===process.platform;function ei(e){["unlink","chmod","stat","lstat","rmdir","readdir"].forEach((t=>{e[t]=e[t]||Yo[t],e[t+="Sync"]=e[t]||Yo[t]})),e.maxBusyTries=e.maxBusyTries||3}function ti(e,t,n){let r=0;"function"==typeof t&&(n=t,t={}),Xo(e,"rimraf: missing path"),Xo.strictEqual(typeof e,"string","rimraf: path should be a string"),Xo.strictEqual(typeof n,"function","rimraf: callback function required"),Xo(t,"rimraf: invalid options argument provided"),Xo.strictEqual(typeof t,"object","rimraf: options should be object"),ei(t),ni(e,t,(function u(o){if(o){if(("EBUSY"===o.code||"ENOTEMPTY"===o.code||"EPERM"===o.code)&&rni(e,t,u)),100*r)}"ENOENT"===o.code&&(o=null)}n(o)}))}function ni(e,t,n){Xo(e),Xo(t),Xo("function"==typeof n),t.lstat(e,((r,u)=>r&&"ENOENT"===r.code?n(null):r&&"EPERM"===r.code&&Qo?ri(e,t,r,n):u&&u.isDirectory()?oi(e,t,r,n):void t.unlink(e,(r=>{if(r){if("ENOENT"===r.code)return n(null);if("EPERM"===r.code)return Qo?ri(e,t,r,n):oi(e,t,r,n);if("EISDIR"===r.code)return oi(e,t,r,n)}return n(r)}))))}function ri(e,t,n,r){Xo(e),Xo(t),Xo("function"==typeof r),n&&Xo(n instanceof Error),t.chmod(e,438,(u=>{u?r("ENOENT"===u.code?null:n):t.stat(e,((u,o)=>{u?r("ENOENT"===u.code?null:n):o.isDirectory()?oi(e,t,n,r):t.unlink(e,r)}))}))}function ui(e,t,n){let r;Xo(e),Xo(t),n&&Xo(n instanceof Error);try{t.chmodSync(e,438)}catch(e){if("ENOENT"===e.code)return;throw n}try{r=t.statSync(e)}catch(e){if("ENOENT"===e.code)return;throw n}r.isDirectory()?si(e,t,n):t.unlinkSync(e)}function oi(e,t,n,r){Xo(e),Xo(t),n&&Xo(n instanceof Error),Xo("function"==typeof r),t.rmdir(e,(u=>{!u||"ENOTEMPTY"!==u.code&&"EEXIST"!==u.code&&"EPERM"!==u.code?u&&"ENOTDIR"===u.code?r(n):r(u):function(e,t,n){Xo(e),Xo(t),Xo("function"==typeof n),t.readdir(e,((r,u)=>{if(r)return n(r);let o,i=u.length;if(0===i)return t.rmdir(e,n);u.forEach((r=>{ti(Zo.join(e,r),t,(r=>{if(!o)return r?n(o=r):void(0==--i&&t.rmdir(e,n))}))}))}))}(e,t,r)}))}function ii(e,t){let n;ei(t=t||{}),Xo(e,"rimraf: missing path"),Xo.strictEqual(typeof e,"string","rimraf: path should be a string"),Xo(t,"rimraf: missing options"),Xo.strictEqual(typeof t,"object","rimraf: options should be object");try{n=t.lstatSync(e)}catch(n){if("ENOENT"===n.code)return;"EPERM"===n.code&&Qo&&ui(e,t,n)}try{n&&n.isDirectory()?si(e,t,null):t.unlinkSync(e)}catch(n){if("ENOENT"===n.code)return;if("EPERM"===n.code)return Qo?ui(e,t,n):si(e,t,n);if("EISDIR"!==n.code)throw n;si(e,t,n)}}function si(e,t,n){Xo(e),Xo(t),n&&Xo(n instanceof Error);try{t.rmdirSync(e)}catch(r){if("ENOTDIR"===r.code)throw n;if("ENOTEMPTY"===r.code||"EEXIST"===r.code||"EPERM"===r.code)!function(e,t){if(Xo(e),Xo(t),t.readdirSync(e).forEach((n=>ii(Zo.join(e,n),t))),!Qo){return t.rmdirSync(e,t)}{const n=Date.now();do{try{return t.rmdirSync(e,t)}catch(e){}}while(Date.now()-n<500)}}(e,t);else if("ENOENT"!==r.code)throw r}}var ci=ti;ti.sync=ii;const ai=ci;var li={remove:(0,ju.fromCallback)(ai),removeSync:ai.sync};const fi=ju.fromCallback,di=oe,Di=e,pi=to,hi=li,Ei=fi((function(e,t){t=t||function(){},di.readdir(e,((n,r)=>{if(n)return pi.mkdirs(e,t);r=r.map((t=>Di.join(e,t))),function e(){const n=r.pop();if(!n)return t();hi.remove(n,(n=>{if(n)return t(n);e()}))}()}))}));function mi(e){let t;try{t=di.readdirSync(e)}catch(t){return pi.mkdirsSync(e)}t.forEach((t=>{t=Di.join(e,t),hi.removeSync(t)}))}var yi={emptyDirSync:mi,emptydirSync:mi,emptyDir:Ei,emptydir:Ei};const Ci=ju.fromCallback,Fi=e,gi=oe,Ai=to,vi=xo.pathExists;var Si={createFile:Ci((function(e,t){function n(){gi.writeFile(e,"",(e=>{if(e)return t(e);t()}))}gi.stat(e,((r,u)=>{if(!r&&u.isFile())return t();const o=Fi.dirname(e);vi(o,((e,r)=>e?t(e):r?n():void Ai.mkdirs(o,(e=>{if(e)return t(e);n()}))))}))})),createFileSync:function(e){let t;try{t=gi.statSync(e)}catch(e){}if(t&&t.isFile())return;const n=Fi.dirname(e);gi.existsSync(n)||Ai.mkdirsSync(n),gi.writeFileSync(e,"")}};const wi=ju.fromCallback,Oi=e,_i=oe,bi=to,Bi=xo.pathExists;var Pi={createLink:wi((function(e,t,n){function r(e,t){_i.link(e,t,(e=>{if(e)return n(e);n(null)}))}Bi(t,((u,o)=>u?n(u):o?n(null):void _i.lstat(e,(u=>{if(u)return u.message=u.message.replace("lstat","ensureLink"),n(u);const o=Oi.dirname(t);Bi(o,((u,i)=>u?n(u):i?r(e,t):void bi.mkdirs(o,(u=>{if(u)return n(u);r(e,t)}))))}))))})),createLinkSync:function(e,t){if(_i.existsSync(t))return;try{_i.lstatSync(e)}catch(e){throw e.message=e.message.replace("lstat","ensureLink"),e}const n=Oi.dirname(t);return _i.existsSync(n)||bi.mkdirsSync(n),_i.linkSync(e,t)}};const Ii=e,ki=oe,xi=xo.pathExists;var Ni={symlinkPaths:function(e,t,n){if(Ii.isAbsolute(e))return ki.lstat(e,(t=>t?(t.message=t.message.replace("lstat","ensureSymlink"),n(t)):n(null,{toCwd:e,toDst:e})));{const r=Ii.dirname(t),u=Ii.join(r,e);return xi(u,((t,o)=>t?n(t):o?n(null,{toCwd:u,toDst:e}):ki.lstat(e,(t=>t?(t.message=t.message.replace("lstat","ensureSymlink"),n(t)):n(null,{toCwd:e,toDst:Ii.relative(r,e)})))))}},symlinkPathsSync:function(e,t){let n;if(Ii.isAbsolute(e)){if(n=ki.existsSync(e),!n)throw new Error("absolute srcpath does not exist");return{toCwd:e,toDst:e}}{const r=Ii.dirname(t),u=Ii.join(r,e);if(n=ki.existsSync(u),n)return{toCwd:u,toDst:e};if(n=ki.existsSync(e),!n)throw new Error("relative srcpath does not exist");return{toCwd:e,toDst:Ii.relative(r,e)}}}};const Ri=oe;var Ti={symlinkType:function(e,t,n){if(n="function"==typeof t?t:n,t="function"!=typeof t&&t)return n(null,t);Ri.lstat(e,((e,r)=>{if(e)return n(null,"file");t=r&&r.isDirectory()?"dir":"file",n(null,t)}))},symlinkTypeSync:function(e,t){let n;if(t)return t;try{n=Ri.lstatSync(e)}catch(e){return"file"}return n&&n.isDirectory()?"dir":"file"}};const Mi=ju.fromCallback,Li=e,ji=oe,$i=to.mkdirs,Hi=to.mkdirsSync,Gi=Ni.symlinkPaths,Vi=Ni.symlinkPathsSync,Ui=Ti.symlinkType,Ji=Ti.symlinkTypeSync,Wi=xo.pathExists;var zi={createSymlink:Mi((function(e,t,n,r){r="function"==typeof n?n:r,n="function"!=typeof n&&n,Wi(t,((u,o)=>u?r(u):o?r(null):void Gi(e,t,((u,o)=>{if(u)return r(u);e=o.toDst,Ui(o.toCwd,n,((n,u)=>{if(n)return r(n);const o=Li.dirname(t);Wi(o,((n,i)=>n?r(n):i?ji.symlink(e,t,u,r):void $i(o,(n=>{if(n)return r(n);ji.symlink(e,t,u,r)}))))}))}))))})),createSymlinkSync:function(e,t,n){if(ji.existsSync(t))return;const r=Vi(e,t);e=r.toDst,n=Ji(r.toCwd,n);const u=Li.dirname(t);return ji.existsSync(u)||Hi(u),ji.symlinkSync(e,t,n)}};var Ki,qi={createFile:Si.createFile,createFileSync:Si.createFileSync,ensureFile:Si.createFile,ensureFileSync:Si.createFileSync,createLink:Pi.createLink,createLinkSync:Pi.createLinkSync,ensureLink:Pi.createLink,ensureLinkSync:Pi.createLinkSync,createSymlink:zi.createSymlink,createSymlinkSync:zi.createSymlinkSync,ensureSymlink:zi.createSymlink,ensureSymlinkSync:zi.createSymlinkSync};try{Ki=oe}catch(e){Ki=n}function Yi(e,t){var n,r="\n";return"object"==typeof t&&null!==t&&(t.spaces&&(n=t.spaces),t.EOL&&(r=t.EOL)),JSON.stringify(e,t?t.replacer:null,n).replace(/\n/g,r)+r}function Zi(e){return Buffer.isBuffer(e)&&(e=e.toString("utf8")),e=e.replace(/^\uFEFF/,"")}var Xi={readFile:function(e,t,n){null==n&&(n=t,t={}),"string"==typeof t&&(t={encoding:t});var r=(t=t||{}).fs||Ki,u=!0;"throws"in t&&(u=t.throws),r.readFile(e,t,(function(r,o){if(r)return n(r);var i;o=Zi(o);try{i=JSON.parse(o,t?t.reviver:null)}catch(t){return u?(t.message=e+": "+t.message,n(t)):n(null,null)}n(null,i)}))},readFileSync:function(e,t){"string"==typeof(t=t||{})&&(t={encoding:t});var n=t.fs||Ki,r=!0;"throws"in t&&(r=t.throws);try{var u=n.readFileSync(e,t);return u=Zi(u),JSON.parse(u,t.reviver)}catch(t){if(r)throw t.message=e+": "+t.message,t;return null}},writeFile:function(e,t,n,r){null==r&&(r=n,n={});var u=(n=n||{}).fs||Ki,o="";try{o=Yi(t,n)}catch(e){return void(r&&r(e,null))}u.writeFile(e,o,n,r)},writeFileSync:function(e,t,n){var r=(n=n||{}).fs||Ki,u=Yi(t,n);return r.writeFileSync(e,u,n)}},Qi=Xi;const es=ju.fromCallback,ts=Qi;var ns={readJson:es(ts.readFile),readJsonSync:ts.readFileSync,writeJson:es(ts.writeFile),writeJsonSync:ts.writeFileSync};const rs=e,us=to,os=xo.pathExists,is=ns;var ss=function(e,t,n,r){"function"==typeof n&&(r=n,n={});const u=rs.dirname(e);os(u,((o,i)=>o?r(o):i?is.writeJson(e,t,n,r):void us.mkdirs(u,(u=>{if(u)return r(u);is.writeJson(e,t,n,r)}))))};const cs=oe,as=e,ls=to,fs=ns;var ds=function(e,t,n){const r=as.dirname(e);cs.existsSync(r)||ls.mkdirsSync(r),fs.writeJsonSync(e,t,n)};const Ds=ju.fromCallback,ps=ns;ps.outputJson=Ds(ss),ps.outputJsonSync=ds,ps.outputJSON=ps.outputJson,ps.outputJSONSync=ps.outputJsonSync,ps.writeJSON=ps.writeJson,ps.writeJSONSync=ps.writeJsonSync,ps.readJSON=ps.readJson,ps.readJSONSync=ps.readJsonSync;var hs=ps;const Es=oe,ms=e,ys=Po.copySync,Cs=li.removeSync,Fs=to.mkdirpSync,gs=Fo;function As(e,t,n){try{Es.renameSync(e,t)}catch(r){if("EXDEV"!==r.code)throw r;return function(e,t,n){const r={overwrite:n,errorOnExist:!0};return ys(e,t,r),Cs(e)}(e,t,n)}}var vs=function(e,t,n){const r=(n=n||{}).overwrite||n.clobber||!1,{srcStat:u}=gs.checkPathsSync(e,t,"move");return gs.checkParentPathsSync(e,u,t,"move"),Fs(ms.dirname(t)),function(e,t,n){if(n)return Cs(t),As(e,t,n);if(Es.existsSync(t))throw new Error("dest already exists.");return As(e,t,n)}(e,t,r)},Ss={moveSync:vs};const ws=oe,Os=e,_s=qo.copy,bs=li.remove,Bs=to.mkdirp,Ps=xo.pathExists,Is=Fo;function ks(e,t,n,r){ws.rename(e,t,(u=>u?"EXDEV"!==u.code?r(u):function(e,t,n,r){const u={overwrite:n,errorOnExist:!0};_s(e,t,u,(t=>t?r(t):bs(e,r)))}(e,t,n,r):r()))}var xs=function(e,t,n,r){"function"==typeof n&&(r=n,n={});const u=n.overwrite||n.clobber||!1;Is.checkPaths(e,t,"move",((n,o)=>{if(n)return r(n);const{srcStat:i}=o;Is.checkParentPaths(e,i,t,"move",(n=>{if(n)return r(n);Bs(Os.dirname(t),(n=>n?r(n):function(e,t,n,r){if(n)return bs(t,(u=>u?r(u):ks(e,t,n,r)));Ps(t,((u,o)=>u?r(u):o?r(new Error("dest already exists.")):ks(e,t,n,r)))}(e,t,u,r)))}))}))};var Ns={move:(0,ju.fromCallback)(xs)};const Rs=ju.fromCallback,Ts=oe,Ms=e,Ls=to,js=xo.pathExists;var $s={outputFile:Rs((function(e,t,n,r){"function"==typeof n&&(r=n,n="utf8");const u=Ms.dirname(e);js(u,((o,i)=>o?r(o):i?Ts.writeFile(e,t,n,r):void Ls.mkdirs(u,(u=>{if(u)return r(u);Ts.writeFile(e,t,n,r)}))))})),outputFileSync:function(e,...t){const n=Ms.dirname(e);if(Ts.existsSync(n))return Ts.writeFileSync(e,...t);Ls.mkdirsSync(n),Ts.writeFileSync(e,...t)}};!function(e){e.exports=Object.assign({},Lu,Po,qo,yi,qi,hs,to,Ss,Ns,$s,xo,li);const t=n;Object.getOwnPropertyDescriptor(t,"promises")&&Object.defineProperty(e.exports,"promises",{get:()=>t.promises})}(Mu);const Hs=Zn.exports("streamroller:fileNameFormatter"),Gs=e;const Vs=Zn.exports("streamroller:fileNameParser"),Us=gr.exports;const Js=Zn.exports("streamroller:moveAndMaybeCompressFile"),Ws=Mu.exports,zs=f;var Ks=async(e,t,n)=>{if(n=function(e){const t={mode:parseInt("0600",8),compress:!1},n=Object.assign({},t,e);return Js(`_parseOption: moveAndMaybeCompressFile called with option=${JSON.stringify(n)}`),n}(n),e!==t){if(await Ws.pathExists(e))if(Js(`moveAndMaybeCompressFile: moving file from ${e} to ${t} ${n.compress?"with":"without"} compress`),n.compress)await new Promise(((r,u)=>{let o=!1;const i=Ws.createWriteStream(t,{mode:n.mode,flags:"wx"}).on("open",(()=>{o=!0;const t=Ws.createReadStream(e).on("open",(()=>{t.pipe(zs.createGzip()).pipe(i)})).on("error",(t=>{Js(`moveAndMaybeCompressFile: error reading ${e}`,t),i.destroy(t)}))})).on("finish",(()=>{Js(`moveAndMaybeCompressFile: finished compressing ${t}, deleting ${e}`),Ws.unlink(e).then(r).catch((t=>{Js(`moveAndMaybeCompressFile: error deleting ${e}, truncating instead`,t),Ws.truncate(e).then(r).catch((t=>{Js(`moveAndMaybeCompressFile: error truncating ${e}`,t),u(t)}))}))})).on("error",(e=>{o?(Js(`moveAndMaybeCompressFile: error writing ${t}, deleting`,e),Ws.unlink(t).then((()=>{u(e)})).catch((e=>{Js(`moveAndMaybeCompressFile: error deleting ${t}`,e),u(e)}))):(Js(`moveAndMaybeCompressFile: error creating ${t}`,e),u(e))}))})).catch((()=>{}));else{Js(`moveAndMaybeCompressFile: renaming ${e} to ${t}`);try{await Ws.move(e,t,{overwrite:!0})}catch(n){if(Js(`moveAndMaybeCompressFile: error renaming ${e} to ${t}`,n),"ENOENT"!==n.code){Js("moveAndMaybeCompressFile: trying copy+truncate instead");try{await Ws.copy(e,t,{overwrite:!0}),await Ws.truncate(e)}catch(e){Js("moveAndMaybeCompressFile: error copy+truncate",e)}}}}}else Js("moveAndMaybeCompressFile: source and target are the same, not doing anything")};const qs=Zn.exports("streamroller:RollingFileWriteStream"),Ys=Mu.exports,Zs=e,Xs=t,Qs=()=>new Date,ec=gr.exports,{Writable:tc}=o,nc=({file:e,keepFileExt:t,needsIndex:n,alwaysIncludeDate:r,compress:u,fileNameSep:o})=>{let i=o||".";const s=Gs.join(e.dir,e.name),c=t=>t+e.ext,a=(e,t,r)=>!n&&r||!t?e:e+i+t,l=(e,t,n)=>(t>0||r)&&n?e+i+n:e,f=(e,t)=>t&&u?e+".gz":e,d=t?[l,a,c,f]:[c,l,a,f];return({date:e,index:t})=>(Hs(`_formatFileName: date=${e}, index=${t}`),d.reduce(((n,r)=>r(n,t,e)),s))},rc=({file:e,keepFileExt:t,pattern:n,fileNameSep:r})=>{let u=r||".";const o="__NOT_MATCHING__";let i=[(e,t)=>e.endsWith(".gz")?(Vs("it is gzipped"),t.isCompressed=!0,e.slice(0,-3)):e,t?t=>t.startsWith(e.name)&&t.endsWith(e.ext)?(Vs("it starts and ends with the right things"),t.slice(e.name.length+1,-1*e.ext.length)):o:t=>t.startsWith(e.base)?(Vs("it starts with the right things"),t.slice(e.base.length+1)):o,n?(e,t)=>{const r=e.split(u);let o=r[r.length-1];Vs("items: ",r,", indexStr: ",o);let i=e;void 0!==o&&o.match(/^\d+$/)?(i=e.slice(0,-1*(o.length+1)),Vs(`dateStr is ${i}`),n&&!i&&(i=o,o="0")):o="0";try{const r=Us.parse(n,i,new Date(0,0));return Us.asString(n,r)!==i?e:(t.index=parseInt(o,10),t.date=i,t.timestamp=r.getTime(),"")}catch(t){return Vs(`Problem parsing ${i} as ${n}, error was: `,t),e}}:(e,t)=>e.match(/^\d+$/)?(Vs("it has an index"),t.index=parseInt(e,10),""):e];return e=>{let t={filename:e,index:0,isCompressed:!1};return i.reduce(((e,n)=>n(e,t)),e)?null:t}},uc=Ks;var oc=class extends tc{constructor(e,t){if(qs(`constructor: creating RollingFileWriteStream. path=${e}`),"string"!=typeof e||0===e.length)throw new Error(`Invalid filename: ${e}`);if(e.endsWith(Zs.sep))throw new Error(`Filename is a directory: ${e}`);0===e.indexOf(`~${Zs.sep}`)&&(e=e.replace("~",Xs.homedir())),super(t),this.options=this._parseOption(t),this.fileObject=Zs.parse(e),""===this.fileObject.dir&&(this.fileObject=Zs.parse(Zs.join(process.cwd(),e))),this.fileFormatter=nc({file:this.fileObject,alwaysIncludeDate:this.options.alwaysIncludePattern,needsIndex:this.options.maxSize 0`)}else delete n.maxSize;if(n.numBackups||0===n.numBackups){if(n.numBackups<0)throw new Error(`options.numBackups (${n.numBackups}) should be >= 0`);if(n.numBackups>=Number.MAX_SAFE_INTEGER)throw new Error(`options.numBackups (${n.numBackups}) should be < Number.MAX_SAFE_INTEGER`);n.numToKeep=n.numBackups+1}else if(n.numToKeep<=0)throw new Error(`options.numToKeep (${n.numToKeep}) should be > 0`);return qs(`_parseOption: creating stream with option=${JSON.stringify(n)}`),n}_final(e){this.currentFileStream.end("",this.options.encoding,e)}_write(e,t,n){this._shouldRoll().then((()=>{qs(`_write: writing chunk. file=${this.currentFileStream.path} state=${JSON.stringify(this.state)} chunk=${e}`),this.currentFileStream.write(e,t,(t=>{this.state.currentSize+=e.length,n(t)}))}))}async _shouldRoll(){(this._dateChanged()||this._tooBig())&&(qs(`_shouldRoll: rolling because dateChanged? ${this._dateChanged()} or tooBig? ${this._tooBig()}`),await this._roll())}_dateChanged(){return this.state.currentDate&&this.state.currentDate!==ec(this.options.pattern,Qs())}_tooBig(){return this.state.currentSize>=this.options.maxSize}_roll(){return qs("_roll: closing the current stream"),new Promise(((e,t)=>{this.currentFileStream.end("",this.options.encoding,(()=>{this._moveOldFiles().then(e).catch(t)}))}))}async _moveOldFiles(){const e=await this._getExistingFiles();for(let t=(this.state.currentDate?e.filter((e=>e.date===this.state.currentDate)):e).length;t>=0;t--){qs(`_moveOldFiles: i = ${t}`);const e=this.fileFormatter({date:this.state.currentDate,index:t}),n=this.fileFormatter({date:this.state.currentDate,index:t+1}),r={compress:this.options.compress&&0===t,mode:this.options.mode};await uc(e,n,r)}this.state.currentSize=0,this.state.currentDate=this.state.currentDate?ec(this.options.pattern,Qs()):null,qs(`_moveOldFiles: finished rolling files. state=${JSON.stringify(this.state)}`),this._renewWriteStream(),await new Promise(((e,t)=>{this.currentFileStream.write("","utf8",(()=>{this._clean().then(e).catch(t)}))}))}async _getExistingFiles(){const e=await Ys.readdir(this.fileObject.dir).catch((()=>[]));qs(`_getExistingFiles: files=${e}`);const t=e.map((e=>this.fileNameParser(e))).filter((e=>e)),n=e=>(e.timestamp?e.timestamp:Qs().getTime())-e.index;return t.sort(((e,t)=>n(e)-n(t))),t}_renewWriteStream(){const e=this.fileFormatter({date:this.state.currentDate,index:0}),t=e=>{try{return Ys.mkdirSync(e,{recursive:!0})}catch(n){if("ENOENT"===n.code)return t(Zs.dirname(e)),t(e);if("EEXIST"!==n.code&&"EROFS"!==n.code)throw n;try{if(Ys.statSync(e).isDirectory())return e;throw n}catch(e){throw n}}};t(this.fileObject.dir);const n={flags:this.options.flags,encoding:this.options.encoding,mode:this.options.mode};var r,u;Ys.appendFileSync(e,"",(r={...n},u="flags",r["flag"]=r[u],delete r[u],r)),this.currentFileStream=Ys.createWriteStream(e,n),this.currentFileStream.on("error",(e=>{this.emit("error",e)}))}async _clean(){const e=await this._getExistingFiles();if(qs(`_clean: numToKeep = ${this.options.numToKeep}, existingFiles = ${e.length}`),qs("_clean: existing files are: ",e),this._tooManyFiles(e.length)){const n=e.slice(0,e.length-this.options.numToKeep).map((e=>Zs.format({dir:this.fileObject.dir,base:e.filename})));await(t=n,qs(`deleteFiles: files to delete: ${t}`),Promise.all(t.map((e=>Ys.unlink(e).catch((t=>{qs(`deleteFiles: error when unlinking ${e}, ignoring. Error was ${t}`)}))))))}var t}_tooManyFiles(e){return this.options.numToKeep>0&&e>this.options.numToKeep}};const ic=oc;var sc=class extends ic{constructor(e,t,n,r){r||(r={}),t&&(r.maxSize=t),r.numBackups||0===r.numBackups||(n||0===n||(n=1),r.numBackups=n),super(e,r),this.backups=r.numBackups,this.size=this.options.maxSize}get theStream(){return this.currentFileStream}};const cc=oc;var ac={RollingFileWriteStream:oc,RollingFileStream:sc,DateRollingFileStream:class extends cc{constructor(e,t,n){t&&"object"==typeof t&&(n=t,t=null),n||(n={}),t||(t="yyyy-MM-dd"),n.pattern=t,n.numBackups||0===n.numBackups?n.daysToKeep=n.numBackups:(n.daysToKeep||0===n.daysToKeep?process.emitWarning("options.daysToKeep is deprecated due to the confusion it causes when used together with file size rolling. Please use options.numBackups instead.","DeprecationWarning","streamroller-DEP0001"):n.daysToKeep=1,n.numBackups=n.daysToKeep),super(e,n),this.mode=this.options.mode}get theStream(){return this.currentFileStream}}};const lc=Zn.exports("log4js:file"),fc=e,dc=ac,Dc=t,pc=Dc.EOL;let hc=!1;const Ec=new Set;function mc(){Ec.forEach((e=>{e.sighupHandler()}))}Tu.configure=function(e,t){let n=t.basicLayout;return e.layout&&(n=t.layout(e.layout.type,e.layout)),e.mode=e.mode||384,function(e,t,n,r,u,o){if("string"!=typeof e||0===e.length)throw new Error(`Invalid filename: ${e}`);if(e.endsWith(fc.sep))throw new Error(`Filename is a directory: ${e}`);function i(e,t,n,r){const u=new dc.RollingFileStream(e,t,n,r);return u.on("error",(t=>{console.error("log4js.fileAppender - Writing to file %s, error happened ",e,t)})),u.on("drain",(()=>{process.emit("log4js:pause",!1)})),u}e=e.replace(new RegExp(`^~(?=${fc.sep}.+)`),Dc.homedir()),e=fc.normalize(e),lc("Creating file appender (",e,", ",n,", ",r=r||0===r?r:5,", ",u,", ",o,")");let s=i(e,n,r,u);const c=function(e){if(s.writable){if(!0===u.removeColor){const t=/\x1b[[0-9;]*m/g;e.data=e.data.map((e=>"string"==typeof e?e.replace(t,""):e))}s.write(t(e,o)+pc,"utf8")||process.emit("log4js:pause",!0)}};return c.reopen=function(){s.end((()=>{s=i(e,n,r,u)}))},c.sighupHandler=function(){lc("SIGHUP handler called."),c.reopen()},c.shutdown=function(e){Ec.delete(c),0===Ec.size&&hc&&(process.removeListener("SIGHUP",mc),hc=!1),s.end("","utf-8",e)},Ec.add(c),hc||(process.on("SIGHUP",mc),hc=!0),c}(e.filename,n,e.maxLogSize,e.backups,e,e.timezoneOffset)};var yc={};const Cc=ac,Fc=t.EOL;function gc(e,t,n,r,u){r.maxSize=r.maxLogSize;const o=function(e,t,n){const r=new Cc.DateRollingFileStream(e,t,n);return r.on("error",(t=>{console.error("log4js.dateFileAppender - Writing to file %s, error happened ",e,t)})),r.on("drain",(()=>{process.emit("log4js:pause",!1)})),r}(e,t,r),i=function(e){o.writable&&(o.write(n(e,u)+Fc,"utf8")||process.emit("log4js:pause",!0))};return i.shutdown=function(e){o.end("","utf-8",e)},i}yc.configure=function(e,t){let n=t.basicLayout;return e.layout&&(n=t.layout(e.layout.type,e.layout)),e.alwaysIncludePattern||(e.alwaysIncludePattern=!1),e.mode=e.mode||384,gc(e.filename,e.pattern,n,e,e.timezoneOffset)};var Ac={};const vc=Zn.exports("log4js:fileSync"),Sc=e,wc=n,Oc=t,_c=Oc.EOL;function bc(e,t){const n=e=>{try{return wc.mkdirSync(e,{recursive:!0})}catch(t){if("ENOENT"===t.code)return n(Sc.dirname(e)),n(e);if("EEXIST"!==t.code&&"EROFS"!==t.code)throw t;try{if(wc.statSync(e).isDirectory())return e;throw t}catch(e){throw t}}};n(Sc.dirname(e)),wc.appendFileSync(e,"",{mode:t.mode,flag:t.flags})}class Bc{constructor(e,t,n,r){if(vc("In RollingFileStream"),t<0)throw new Error(`maxLogSize (${t}) should be > 0`);this.filename=e,this.size=t,this.backups=n,this.options=r,this.currentSize=0,this.currentSize=function(e){let t=0;try{t=wc.statSync(e).size}catch(t){bc(e,r)}return t}(this.filename)}shouldRoll(){return vc("should roll with current size %d, and max size %d",this.currentSize,this.size),this.currentSize>=this.size}roll(e){const t=this,n=new RegExp(`^${Sc.basename(e)}`);function r(e){return n.test(e)}function u(t){return parseInt(t.slice(`${Sc.basename(e)}.`.length),10)||0}function o(e,t){return u(e)-u(t)}function i(n){const r=u(n);if(vc(`Index of ${n} is ${r}`),0===t.backups)wc.truncateSync(e,0);else if(r ${e}.${r+1}`),wc.renameSync(Sc.join(Sc.dirname(e),n),`${e}.${r+1}`)}}vc("Rolling, rolling, rolling"),vc("Renaming the old files"),wc.readdirSync(Sc.dirname(e)).filter(r).sort(o).reverse().forEach(i)}write(e,t){const n=this;vc("in write"),this.shouldRoll()&&(this.currentSize=0,this.roll(this.filename)),vc("writing the chunk to the file"),n.currentSize+=e.length,wc.appendFileSync(n.filename,e)}}Ac.configure=function(e,t){let n=t.basicLayout;e.layout&&(n=t.layout(e.layout.type,e.layout));const r={flags:e.flags||"a",encoding:e.encoding||"utf8",mode:e.mode||384};return function(e,t,n,r,u,o){if("string"!=typeof e||0===e.length)throw new Error(`Invalid filename: ${e}`);if(e.endsWith(Sc.sep))throw new Error(`Filename is a directory: ${e}`);e=e.replace(new RegExp(`^~(?=${Sc.sep}.+)`),Oc.homedir()),e=Sc.normalize(e),vc("Creating fileSync appender (",e,", ",n,", ",r=r||0===r?r:5,", ",u,", ",o,")");const i=function(e,t,n){let r;var o;return t?r=new Bc(e,t,n,u):(bc(o=e,u),r={write(e){wc.appendFileSync(o,e)}}),r}(e,n,r);return e=>{i.write(t(e,o)+_c)}}(e.filename,n,e.maxLogSize,e.backups,r,e.timezoneOffset)};var Pc={};const Ic=Zn.exports("log4js:tcp"),kc=d;Pc.configure=function(e,t){Ic(`configure with config = ${e}`);let n=function(e){return e.serialise()};return e.layout&&(n=t.layout(e.layout.type,e.layout)),function(e,t){let n=!1;const r=[];let u,o=3,i="__LOG4JS__";function s(e){Ic("Writing log event to socket"),n=u.write(`${t(e)}${i}`,"utf8")}function c(){let e;for(Ic("emptying buffer");e=r.shift();)s(e)}function a(e){n?s(e):(Ic("buffering log event because it cannot write at the moment"),r.push(e))}return function t(){Ic(`appender creating socket to ${e.host||"localhost"}:${e.port||5e3}`),i=`${e.endMsg||"__LOG4JS__"}`,u=kc.createConnection(e.port||5e3,e.host||"localhost"),u.on("connect",(()=>{Ic("socket connected"),c(),n=!0})),u.on("drain",(()=>{Ic("drain event received, emptying buffer"),n=!0,c()})),u.on("timeout",u.end.bind(u)),u.on("error",(e=>{Ic("connection error",e),n=!1,c()})),u.on("close",t)}(),a.shutdown=function(e){Ic("shutdown called"),r.length&&o?(Ic("buffer has items, waiting 100ms to empty"),o-=1,setTimeout((()=>{a.shutdown(e)}),100)):(u.removeAllListeners("close"),u.end(e))},a}(e,n)};const xc=e,Nc=Zn.exports("log4js:appenders"),Rc=Fr,Tc=Au,Mc=Gr,Lc=Lr,jc=vu,$c=new Map;$c.set("console",_u),$c.set("stdout",Bu),$c.set("stderr",Pu),$c.set("logLevelFilter",Iu),$c.set("categoryFilter",ku),$c.set("noLogFilter",Nu),$c.set("file",Tu),$c.set("dateFile",yc),$c.set("fileSync",Ac),$c.set("tcp",Pc);const Hc=new Map,Gc=(e,t)=>{let n;try{const t=`${e}.cjs`;n=require.resolve(t),Nc("Loading module from ",t)}catch(t){n=e,Nc("Loading module from ",e)}try{return require(n)}catch(n){return void Rc.throwExceptionIf(t,"MODULE_NOT_FOUND"!==n.code,`appender "${e}" could not be loaded (error was: ${n})`)}},Vc=new Set,Uc=(e,t)=>{if(Hc.has(e))return Hc.get(e);if(!t.appenders[e])return!1;if(Vc.has(e))throw new Error(`Dependency loop detected for appender ${e}.`);Vc.add(e),Nc(`Creating appender ${e}`);const n=Jc(e,t);return Vc.delete(e),Hc.set(e,n),n},Jc=(e,t)=>{const n=t.appenders[e],r=n.type.configure?n.type:((e,t)=>$c.get(e)||Gc(`./${e}`,t)||Gc(e,t)||require.main&&require.main.filename&&Gc(xc.join(xc.dirname(require.main.filename),e),t)||Gc(xc.join(process.cwd(),e),t))(n.type,t);return Rc.throwExceptionIf(t,Rc.not(r),`appender "${e}" is not valid (type "${n.type}" could not be found)`),r.appender&&(process.emitWarning(`Appender ${n.type} exports an appender function.`,"DeprecationWarning","log4js-node-DEP0001"),Nc("[log4js-node-DEP0001]",`DEPRECATION: Appender ${n.type} exports an appender function.`)),r.shutdown&&(process.emitWarning(`Appender ${n.type} exports a shutdown function.`,"DeprecationWarning","log4js-node-DEP0002"),Nc("[log4js-node-DEP0002]",`DEPRECATION: Appender ${n.type} exports a shutdown function.`)),Nc(`${e}: clustering.isMaster ? ${Tc.isMaster()}`),Nc(`${e}: appenderModule is ${i.inspect(r)}`),Tc.onlyOnMaster((()=>(Nc(`calling appenderModule.configure for ${e} / ${n.type}`),r.configure(jc.modifyConfig(n),Lc,(e=>Uc(e,t)),Mc))),(()=>{}))},Wc=e=>{if(Hc.clear(),Vc.clear(),!e)return;const t=[];Object.values(e.categories).forEach((e=>{t.push(...e.appenders)})),Object.keys(e.appenders).forEach((n=>{(t.includes(n)||"tcp-server"===e.appenders[n].type||"multiprocess"===e.appenders[n].type)&&Uc(n,e)}))},zc=()=>{Wc()};zc(),Rc.addListener((e=>{Rc.throwExceptionIf(e,Rc.not(Rc.anObject(e.appenders)),'must have a property "appenders" of type object.');const t=Object.keys(e.appenders);Rc.throwExceptionIf(e,Rc.not(t.length),"must define at least one appender."),t.forEach((t=>{Rc.throwExceptionIf(e,Rc.not(e.appenders[t].type),`appender "${t}" is not valid (must be an object with property "type")`)}))})),Rc.addListener(Wc),Vr.exports=Hc,Vr.exports.init=zc;var Kc={exports:{}};!function(e){const t=Zn.exports("log4js:categories"),n=Fr,r=Gr,u=Vr.exports,o=new Map;function i(e,t,n){if(!1===t.inherit)return;const r=n.lastIndexOf(".");if(r<0)return;const u=n.slice(0,r);let o=e.categories[u];o||(o={inherit:!0,appenders:[]}),i(e,o,u),!e.categories[u]&&o.appenders&&o.appenders.length&&o.level&&(e.categories[u]=o),t.appenders=t.appenders||[],t.level=t.level||o.level,o.appenders.forEach((e=>{t.appenders.includes(e)||t.appenders.push(e)})),t.parent=o}function s(e){if(!e.categories)return;Object.keys(e.categories).forEach((t=>{const n=e.categories[t];i(e,n,t)}))}n.addPreProcessingListener((e=>s(e))),n.addListener((e=>{n.throwExceptionIf(e,n.not(n.anObject(e.categories)),'must have a property "categories" of type object.');const t=Object.keys(e.categories);n.throwExceptionIf(e,n.not(t.length),"must define at least one category."),t.forEach((t=>{const o=e.categories[t];n.throwExceptionIf(e,[n.not(o.appenders),n.not(o.level)],`category "${t}" is not valid (must be an object with properties "appenders" and "level")`),n.throwExceptionIf(e,n.not(Array.isArray(o.appenders)),`category "${t}" is not valid (appenders must be an array of appender names)`),n.throwExceptionIf(e,n.not(o.appenders.length),`category "${t}" is not valid (appenders must contain at least one appender name)`),Object.prototype.hasOwnProperty.call(o,"enableCallStack")&&n.throwExceptionIf(e,"boolean"!=typeof o.enableCallStack,`category "${t}" is not valid (enableCallStack must be boolean type)`),o.appenders.forEach((r=>{n.throwExceptionIf(e,n.not(u.get(r)),`category "${t}" is not valid (appender "${r}" is not defined)`)})),n.throwExceptionIf(e,n.not(r.getLevel(o.level)),`category "${t}" is not valid (level "${o.level}" not recognised; valid levels are ${r.levels.join(", ")})`)})),n.throwExceptionIf(e,n.not(e.categories.default),'must define a "default" category.')}));const c=e=>{if(o.clear(),!e)return;Object.keys(e.categories).forEach((n=>{const i=e.categories[n],s=[];i.appenders.forEach((e=>{s.push(u.get(e)),t(`Creating category ${n}`),o.set(n,{appenders:s,level:r.getLevel(i.level),enableCallStack:i.enableCallStack||!1})}))}))},a=()=>{c()};a(),n.addListener(c);const l=e=>{if(t(`configForCategory: searching for config for ${e}`),o.has(e))return t(`configForCategory: ${e} exists in config, returning it`),o.get(e);let n;return e.indexOf(".")>0?(t(`configForCategory: ${e} has hierarchy, cloning from parents`),n={...l(e.slice(0,e.lastIndexOf(".")))}):(o.has("default")||c({categories:{default:{appenders:["out"],level:"OFF"}}}),t("configForCategory: cloning default category"),n={...o.get("default")}),o.set(e,n),n};e.exports=o,e.exports=Object.assign(e.exports,{appendersForCategory:e=>l(e).appenders,getLevelForCategory:e=>l(e).level,setLevelForCategory:(e,t)=>{l(e).level=t},getEnableCallStackForCategory:e=>!0===l(e).enableCallStack,setEnableCallStackForCategory:(e,t)=>{l(e).enableCallStack=t},init:a})}(Kc);const qc=Zn.exports("log4js:logger"),Yc=au,Zc=Gr,Xc=Au,Qc=Kc.exports,ea=Fr,ta=/at (?:(.+)\s+\()?(?:(.+?):(\d+)(?::(\d+))?|([^)]+))\)?/;function na(e,t=4){try{const n=e.stack.split("\n").slice(t);if(!n.length)return null;const r=ta.exec(n[0]);if(r&&6===r.length){let e="",t="",u="";return r[1]&&""!==r[1]&&([t,u]=r[1].replace(/[[\]]/g,"").split(" as "),u=u||"",t.includes(".")&&([e,t]=t.split("."))),{fileName:r[2],lineNumber:parseInt(r[3],10),columnNumber:parseInt(r[4],10),callStack:n.join("\n"),className:e,functionName:t,functionAlias:u,callerName:r[1]||""}}console.error("log4js.logger - defaultParseCallStack error")}catch(e){console.error("log4js.logger - defaultParseCallStack error",e)}return null}let ra=class{constructor(e){if(!e)throw new Error("No category provided.");this.category=e,this.context={},this.callStackSkipIndex=0,this.parseCallStack=na,qc(`Logger created (${this.category}, ${this.level})`)}get level(){return Zc.getLevel(Qc.getLevelForCategory(this.category),Zc.OFF)}set level(e){Qc.setLevelForCategory(this.category,Zc.getLevel(e,this.level))}get useCallStack(){return Qc.getEnableCallStackForCategory(this.category)}set useCallStack(e){Qc.setEnableCallStackForCategory(this.category,!0===e)}get callStackLinesToSkip(){return this.callStackSkipIndex}set callStackLinesToSkip(e){if("number"!=typeof e)throw new TypeError("Must be a number");if(e<0)throw new RangeError("Must be >= 0");this.callStackSkipIndex=e}log(e,...t){const n=Zc.getLevel(e);n?this.isLevelEnabled(n)&&this._log(n,t):ea.validIdentifier(e)&&t.length>0?(this.log(Zc.WARN,"log4js:logger.log: valid log-level not found as first parameter given:",e),this.log(Zc.INFO,`[${e}]`,...t)):this.log(Zc.INFO,e,...t)}isLevelEnabled(e){return this.level.isLessThanOrEqualTo(e)}_log(e,t){qc(`sending log data (${e}) to appenders`);const n=t.find((e=>e instanceof Error));let r;if(this.useCallStack){try{n&&(r=this.parseCallStack(n,this.callStackSkipIndex+1))}catch(e){}r=r||this.parseCallStack(new Error,this.callStackSkipIndex+3+1)}const u=new Yc(this.category,e,t,this.context,r,n);Xc.send(u)}addContext(e,t){this.context[e]=t}removeContext(e){delete this.context[e]}clearContext(){this.context={}}setParseCallStackFunction(e){if("function"==typeof e)this.parseCallStack=e;else{if(void 0!==e)throw new TypeError("Invalid type passed to setParseCallStackFunction");this.parseCallStack=na}}};function ua(e){const t=Zc.getLevel(e),n=t.toString().toLowerCase().replace(/_([a-z])/g,(e=>e[1].toUpperCase())),r=n[0].toUpperCase()+n.slice(1);ra.prototype[`is${r}Enabled`]=function(){return this.isLevelEnabled(t)},ra.prototype[n]=function(...e){this.log(t,...e)}}Zc.levels.forEach(ua),ea.addListener((()=>{Zc.levels.forEach(ua)}));var oa=ra;const ia=Gr;function sa(e){return e.originalUrl||e.url}function ca(e,t){for(let n=0;n{if(void 0!==e._logging)return i();if("function"!=typeof t.nolog){const n=function(e){let t=null;if(e instanceof RegExp&&(t=e),"string"==typeof e&&(t=new RegExp(e)),Array.isArray(e)){const n=e.map((e=>e.source?e.source:e));t=new RegExp(n.join("|"))}return t}(t.nolog);if(n&&n.test(e.originalUrl))return i()}if(n.isLevelEnabled(r)||"auto"===t.level){const i=new Date,{writeHead:s}=o;e._logging=!0,o.writeHead=(e,t)=>{o.writeHead=s,o.writeHead(e,t),o.__statusCode=e,o.__headers=t||{}};let c=!1;const a=()=>{if(c)return;if(c=!0,"function"==typeof t.nolog&&!0===t.nolog(e,o))return void(e._logging=!1);o.responseTime=new Date-i,o.statusCode&&"auto"===t.level&&(r=ia.INFO,o.statusCode>=300&&(r=ia.WARN),o.statusCode>=400&&(r=ia.ERROR)),r=function(e,t,n){let r=t;if(n){const t=n.find((t=>{let n=!1;return n=t.from&&t.to?e>=t.from&&e<=t.to:-1!==t.codes.indexOf(e),n}));t&&(r=ia.getLevel(t.level,r))}return r}(o.statusCode,r,t.statusRules);const s=function(e,t,n){const r=[];return r.push({token:":url",replacement:sa(e)}),r.push({token:":protocol",replacement:e.protocol}),r.push({token:":hostname",replacement:e.hostname}),r.push({token:":method",replacement:e.method}),r.push({token:":status",replacement:t.__statusCode||t.statusCode}),r.push({token:":response-time",replacement:t.responseTime}),r.push({token:":date",replacement:(new Date).toUTCString()}),r.push({token:":referrer",replacement:e.headers.referer||e.headers.referrer||""}),r.push({token:":http-version",replacement:`${e.httpVersionMajor}.${e.httpVersionMinor}`}),r.push({token:":remote-addr",replacement:e.headers["x-forwarded-for"]||e.ip||e._remoteAddress||e.socket&&(e.socket.remoteAddress||e.socket.socket&&e.socket.socket.remoteAddress)}),r.push({token:":user-agent",replacement:e.headers["user-agent"]}),r.push({token:":content-length",replacement:t.getHeader("content-length")||t.__headers&&t.__headers["Content-Length"]||"-"}),r.push({token:/:req\[([^\]]+)]/g,replacement:(t,n)=>e.headers[n.toLowerCase()]}),r.push({token:/:res\[([^\]]+)]/g,replacement:(e,n)=>t.getHeader(n.toLowerCase())||t.__headers&&t.__headers[n]}),(e=>{const t=e.concat();for(let e=0;eca(e,s)));t&&n.log(r,t)}else n.log(r,ca(u,s));t.context&&n.removeContext("res")};o.on("end",a),o.on("finish",a),o.on("error",a),o.on("close",a)}return i()}},wa=Da;let Oa=!1;function _a(e){if(!Oa)return;pa("Received log event ",e);ga.appendersForCategory(e.categoryName).forEach((t=>{t(e)}))}function ba(e){Oa&&Ba();let t=e;return"string"==typeof t&&(t=function(e){pa(`Loading configuration from ${e}`);try{return JSON.parse(ha.readFileSync(e,"utf8"))}catch(t){throw new Error(`Problem reading config from file "${e}". Error was ${t.message}`,t)}}(e)),pa(`Configuration is ${t}`),ma.configure(Ea(t)),va.onMessage(_a),Oa=!0,Pa}function Ba(e=(()=>{})){if("function"!=typeof e)throw new TypeError("Invalid callback passed to shutdown");pa("Shutdown called. Disabling all log writing."),Oa=!1;const t=Array.from(Fa.values());Fa.init(),ga.init();const n=t.reduce(((e,t)=>t.shutdown?e+1:e),0);0===n&&(pa("No appenders with shutdown functions found."),e());let r,u=0;function o(t){r=r||t,u+=1,pa(`Appender shutdowns complete: ${u} / ${n}`),u>=n&&(pa("All shutdown functions completed."),e(r))}pa(`Found ${n} appenders with shutdown functions.`),t.filter((e=>e.shutdown)).forEach((e=>e.shutdown(o)))}const Pa={getLogger:function(e){return Oa||ba(process.env.LOG4JS_CONFIG||{appenders:{out:{type:"stdout"}},categories:{default:{appenders:["out"],level:"OFF"}}}),new Aa(e||"default")},configure:ba,shutdown:Ba,connectLogger:Sa,levels:Ca,addLayout:ya.addLayout,recording:function(){return wa}};var Ia=Pa;!function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.addCustomPlugin=e.addCustomTask=e.hvigorTrace=void 0;const t=Ia;e.hvigorTrace={totalTime:0,moduleNum:0,taskTime:{},isIncremental:!0,hasIncremental:!1,isParallel:!0,IS_DAEMON:!0,LOG_LEVEL:t.levels.INFO.levelStr,IS_HVIGORFILE_TYPE_CHECK:!1},e.addCustomTask=function(t){var n;let r=null!==(n=e.hvigorTrace.CUSTOM_TASKS)&&void 0!==n?n:[];r.length>0&&(r=r.filter((e=>e.NAME!==t.NAME))),r.push(t),e.hvigorTrace.CUSTOM_TASKS=r},e.addCustomPlugin=function(t){var n;let r=null!==(n=e.hvigorTrace.CUSTOM_PLUGINS)&&void 0!==n?n:[];r.length>0&&(r=r.filter((e=>e.PLUGIN_ID!==t.PLUGIN_ID))),r.push({PLUGIN_ID:t.PLUGIN_ID}),e.hvigorTrace.CUSTOM_PLUGINS=r}}(Yn);var ka,xa={};ka=xa,Object.defineProperty(ka,"__esModule",{value:!0}),ka.isCI=void 0,ka.isCI=function(){return!("false"===process.env.CI||!(process.env.BUILD_ID||process.env.BUILD_NUMBER||process.env.CI||process.env.CI_APP_ID||process.env.CI_BUILD_ID||process.env.CI_BUILD_NUMBER||process.env.CI_NAME||process.env.CONTINUOUS_INTEGRATION||process.env.RUN_ID||ka.name))};var Na={};!function(e){var t=p&&p.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(e,"__esModule",{value:!0}),e.hashFile=e.hash=e.createHash=void 0;const r=t(D),u=t(n);e.createHash=(e="MD5")=>r.default.createHash(e);e.hash=(t,n)=>(0,e.createHash)(n).update(t).digest("hex");e.hashFile=(t,n)=>{if(u.default.existsSync(t))return(0,e.hash)(u.default.readFileSync(t,"utf-8"),n)}}(Na);var Ra={},Ta={},Ma={};Object.defineProperty(Ma,"__esModule",{value:!0}),Ma.Unicode=void 0;class La{}Ma.Unicode=La,La.SPACE_SEPARATOR=/[\u1680\u2000-\u200A\u202F\u205F\u3000]/,La.ID_START=/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE83\uDE86-\uDE89\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]/,La.ID_CONTINUE=/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u08D4-\u08E1\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u09FC\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9-\u0AFF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C80-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D00-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D54-\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1CD0-\u1CD2\u1CD4-\u1CF9\u1D00-\u1DF9\u1DFB-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE3E\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC00-\uDC4A\uDC50-\uDC59\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDE00-\uDE3E\uDE47\uDE50-\uDE83\uDE86-\uDE99\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC36\uDC38-\uDC40\uDC50-\uDC59\uDC72-\uDC8F\uDC92-\uDCA7\uDCA9-\uDCB6\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD36\uDD3A\uDD3C\uDD3D\uDD3F-\uDD47\uDD50-\uDD59]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6\uDD00-\uDD4A\uDD50-\uDD59]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/,Object.defineProperty(Ta,"__esModule",{value:!0}),Ta.JudgeUtil=void 0;const ja=Ma;Ta.JudgeUtil=class{static isIgnoreChar(e){return"string"==typeof e&&("\t"===e||"\v"===e||"\f"===e||" "===e||" "===e||"\ufeff"===e||"\n"===e||"\r"===e||"\u2028"===e||"\u2029"===e)}static isSpaceSeparator(e){return"string"==typeof e&&ja.Unicode.SPACE_SEPARATOR.test(e)}static isIdStartChar(e){return"string"==typeof e&&(e>="a"&&e<="z"||e>="A"&&e<="Z"||"$"===e||"_"===e||ja.Unicode.ID_START.test(e))}static isIdContinueChar(e){return"string"==typeof e&&(e>="a"&&e<="z"||e>="A"&&e<="Z"||e>="0"&&e<="9"||"$"===e||"_"===e||"‌"===e||"‍"===e||ja.Unicode.ID_CONTINUE.test(e))}static isDigitWithoutZero(e){return/[1-9]/.test(e)}static isDigit(e){return"string"==typeof e&&/[0-9]/.test(e)}static isHexDigit(e){return"string"==typeof e&&/[0-9A-Fa-f]/.test(e)}};var $a=p&&p.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(Ra,"__esModule",{value:!0}),Ra.parseJsonText=Ra.parseJsonFile=void 0;const Ha=$a(n),Ga=$a(t),Va=$a(e),Ua=Ta;var Ja;!function(e){e[e.Char=0]="Char",e[e.EOF=1]="EOF",e[e.Identifier=2]="Identifier"}(Ja||(Ja={}));let Wa,za,Ka,qa,Ya,Za,Xa="start",Qa=[],el=0,tl=1,nl=0,rl=!1,ul="default",ol="'",il=1;function sl(e,t=!1){za=String(e),Xa="start",Qa=[],el=0,tl=1,nl=0,qa=void 0,rl=t;do{Wa=cl(),hl[Xa]()}while("eof"!==Wa.type);return qa}function cl(){for(ul="default",Ya="",ol="'",il=1;;){Za=al();const e=fl[ul]();if(e)return e}}function al(){if(za[el])return String.fromCodePoint(za.codePointAt(el))}function ll(){const e=al();return"\n"===e?(tl++,nl=0):e?nl+=e.length:nl++,e&&(el+=e.length),e}Ra.parseJsonFile=function(e,t=!1,n="utf-8"){const r=Ha.default.readFileSync(Va.default.resolve(e),{encoding:n});try{return sl(r,t)}catch(t){if(t instanceof SyntaxError){const n=t.message.split("at");if(2===n.length)throw new Error(`${n[0].trim()}${Ga.default.EOL}\t at ${e}:${n[1].trim()}`)}throw new Error(`${e} is not in valid JSON/JSON5 format.`)}},Ra.parseJsonText=sl;const fl={default(){switch(Za){case"/":return ll(),void(ul="comment");case void 0:return ll(),dl("eof")}if(!Ua.JudgeUtil.isIgnoreChar(Za)&&!Ua.JudgeUtil.isSpaceSeparator(Za))return fl[Xa]();ll()},start(){ul="value"},beforePropertyName(){switch(Za){case"$":case"_":return Ya=ll(),void(ul="identifierName");case"\\":return ll(),void(ul="identifierNameStartEscape");case"}":return dl("punctuator",ll());case'"':case"'":return ol=Za,ll(),void(ul="string")}if(Ua.JudgeUtil.isIdStartChar(Za))return Ya+=ll(),void(ul="identifierName");throw Cl(Ja.Char,ll())},afterPropertyName(){if(":"===Za)return dl("punctuator",ll());throw Cl(Ja.Char,ll())},beforePropertyValue(){ul="value"},afterPropertyValue(){switch(Za){case",":case"}":return dl("punctuator",ll())}throw Cl(Ja.Char,ll())},beforeArrayValue(){if("]"===Za)return dl("punctuator",ll());ul="value"},afterArrayValue(){switch(Za){case",":case"]":return dl("punctuator",ll())}throw Cl(Ja.Char,ll())},end(){throw Cl(Ja.Char,ll())},comment(){switch(Za){case"*":return ll(),void(ul="multiLineComment");case"/":return ll(),void(ul="singleLineComment")}throw Cl(Ja.Char,ll())},multiLineComment(){switch(Za){case"*":return ll(),void(ul="multiLineCommentAsterisk");case void 0:throw Cl(Ja.Char,ll())}ll()},multiLineCommentAsterisk(){switch(Za){case"*":return void ll();case"/":return ll(),void(ul="default");case void 0:throw Cl(Ja.Char,ll())}ll(),ul="multiLineComment"},singleLineComment(){switch(Za){case"\n":case"\r":case"\u2028":case"\u2029":return ll(),void(ul="default");case void 0:return ll(),dl("eof")}ll()},value(){switch(Za){case"{":case"[":return dl("punctuator",ll());case"n":return ll(),Dl("ull"),dl("null",null);case"t":return ll(),Dl("rue"),dl("boolean",!0);case"f":return ll(),Dl("alse"),dl("boolean",!1);case"-":case"+":return"-"===ll()&&(il=-1),void(ul="numerical");case".":case"0":case"I":case"N":return void(ul="numerical");case'"':case"'":return ol=Za,ll(),Ya="",void(ul="string")}if(void 0===Za||!Ua.JudgeUtil.isDigitWithoutZero(Za))throw Cl(Ja.Char,ll());ul="numerical"},numerical(){switch(Za){case".":return Ya=ll(),void(ul="decimalPointLeading");case"0":return Ya=ll(),void(ul="zero");case"I":return ll(),Dl("nfinity"),dl("numeric",il*(1/0));case"N":return ll(),Dl("aN"),dl("numeric",NaN)}if(void 0!==Za&&Ua.JudgeUtil.isDigitWithoutZero(Za))return Ya=ll(),void(ul="decimalInteger");throw Cl(Ja.Char,ll())},zero(){switch(Za){case".":case"e":case"E":return void(ul="decimal");case"x":case"X":return Ya+=ll(),void(ul="hexadecimal")}return dl("numeric",0)},decimalInteger(){switch(Za){case".":case"e":case"E":return void(ul="decimal")}if(!Ua.JudgeUtil.isDigit(Za))return dl("numeric",il*Number(Ya));Ya+=ll()},decimal(){switch(Za){case".":Ya+=ll(),ul="decimalFraction";break;case"e":case"E":Ya+=ll(),ul="decimalExponent"}},decimalPointLeading(){if(Ua.JudgeUtil.isDigit(Za))return Ya+=ll(),void(ul="decimalFraction");throw Cl(Ja.Char,ll())},decimalFraction(){switch(Za){case"e":case"E":return Ya+=ll(),void(ul="decimalExponent")}if(!Ua.JudgeUtil.isDigit(Za))return dl("numeric",il*Number(Ya));Ya+=ll()},decimalExponent(){switch(Za){case"+":case"-":return Ya+=ll(),void(ul="decimalExponentSign")}if(Ua.JudgeUtil.isDigit(Za))return Ya+=ll(),void(ul="decimalExponentInteger");throw Cl(Ja.Char,ll())},decimalExponentSign(){if(Ua.JudgeUtil.isDigit(Za))return Ya+=ll(),void(ul="decimalExponentInteger");throw Cl(Ja.Char,ll())},decimalExponentInteger(){if(!Ua.JudgeUtil.isDigit(Za))return dl("numeric",il*Number(Ya));Ya+=ll()},hexadecimal(){if(Ua.JudgeUtil.isHexDigit(Za))return Ya+=ll(),void(ul="hexadecimalInteger");throw Cl(Ja.Char,ll())},hexadecimalInteger(){if(!Ua.JudgeUtil.isHexDigit(Za))return dl("numeric",il*Number(Ya));Ya+=ll()},identifierNameStartEscape(){if("u"!==Za)throw Cl(Ja.Char,ll());ll();const e=pl();switch(e){case"$":case"_":break;default:if(!Ua.JudgeUtil.isIdStartChar(e))throw Cl(Ja.Identifier)}Ya+=e,ul="identifierName"},identifierName(){switch(Za){case"$":case"_":case"‌":case"‍":return void(Ya+=ll());case"\\":return ll(),void(ul="identifierNameEscape")}if(!Ua.JudgeUtil.isIdContinueChar(Za))return dl("identifier",Ya);Ya+=ll()},identifierNameEscape(){if("u"!==Za)throw Cl(Ja.Char,ll());ll();const e=pl();switch(e){case"$":case"_":case"‌":case"‍":break;default:if(!Ua.JudgeUtil.isIdContinueChar(e))throw Cl(Ja.Identifier)}Ya+=e,ul="identifierName"},string(){switch(Za){case"\\":return ll(),void(Ya+=function(){const e=al(),t=function(){switch(al()){case"b":return ll(),"\b";case"f":return ll(),"\f";case"n":return ll(),"\n";case"r":return ll(),"\r";case"t":return ll(),"\t";case"v":return ll(),"\v"}return}();if(t)return t;switch(e){case"0":if(ll(),Ua.JudgeUtil.isDigit(al()))throw Cl(Ja.Char,ll());return"\0";case"x":return ll(),function(){let e="",t=al();if(!Ua.JudgeUtil.isHexDigit(t))throw Cl(Ja.Char,ll());if(e+=ll(),t=al(),!Ua.JudgeUtil.isHexDigit(t))throw Cl(Ja.Char,ll());return e+=ll(),String.fromCodePoint(parseInt(e,16))}();case"u":return ll(),pl();case"\n":case"\u2028":case"\u2029":return ll(),"";case"\r":return ll(),"\n"===al()&&ll(),""}if(void 0===e||Ua.JudgeUtil.isDigitWithoutZero(e))throw Cl(Ja.Char,ll());return ll()}());case'"':case"'":if(Za===ol){const e=dl("string",Ya);return ll(),e}return void(Ya+=ll());case"\n":case"\r":case void 0:throw Cl(Ja.Char,ll());case"\u2028":case"\u2029":!function(e){console.warn(`JSON5: '${yl(e)}' in strings is not valid ECMAScript; consider escaping.`)}(Za)}Ya+=ll()}};function dl(e,t){return{type:e,value:t,line:tl,column:nl}}function Dl(e){for(const t of e){if(al()!==t)throw Cl(Ja.Char,ll());ll()}}function pl(){let e="",t=4;for(;t-- >0;){const t=al();if(!Ua.JudgeUtil.isHexDigit(t))throw Cl(Ja.Char,ll());e+=ll()}return String.fromCodePoint(parseInt(e,16))}const hl={start(){if("eof"===Wa.type)throw Cl(Ja.EOF);El()},beforePropertyName(){switch(Wa.type){case"identifier":case"string":return Ka=Wa.value,void(Xa="afterPropertyName");case"punctuator":return void ml();case"eof":throw Cl(Ja.EOF)}},afterPropertyName(){if("eof"===Wa.type)throw Cl(Ja.EOF);Xa="beforePropertyValue"},beforePropertyValue(){if("eof"===Wa.type)throw Cl(Ja.EOF);El()},afterPropertyValue(){if("eof"===Wa.type)throw Cl(Ja.EOF);switch(Wa.value){case",":return void(Xa="beforePropertyName");case"}":ml()}},beforeArrayValue(){if("eof"===Wa.type)throw Cl(Ja.EOF);"punctuator"!==Wa.type||"]"!==Wa.value?El():ml()},afterArrayValue(){if("eof"===Wa.type)throw Cl(Ja.EOF);switch(Wa.value){case",":return void(Xa="beforeArrayValue");case"]":ml()}},end(){}};function El(){const e=function(){let e;switch(Wa.type){case"punctuator":switch(Wa.value){case"{":e={};break;case"[":e=[]}break;case"null":case"boolean":case"numeric":case"string":e=Wa.value}return e}();if(rl&&"object"==typeof e&&(e._line=tl,e._column=nl),void 0===qa)qa=e;else{const t=Qa[Qa.length-1];Array.isArray(t)?rl&&"object"!=typeof e?t.push({value:e,_line:tl,_column:nl}):t.push(e):t[Ka]=rl&&"object"!=typeof e?{value:e,_line:tl,_column:nl}:e}!function(e){if(e&&"object"==typeof e)Qa.push(e),Xa=Array.isArray(e)?"beforeArrayValue":"beforePropertyName";else{const e=Qa[Qa.length-1];Xa=e?Array.isArray(e)?"afterArrayValue":"afterPropertyValue":"end"}}(e)}function ml(){Qa.pop();const e=Qa[Qa.length-1];Xa=e?Array.isArray(e)?"afterArrayValue":"afterPropertyValue":"end"}function yl(e){const t={"'":"\\'",'"':'\\"',"\\":"\\\\","\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\v":"\\v","\0":"\\0","\u2028":"\\u2028","\u2029":"\\u2029"};if(t[e])return t[e];if(e<" "){const t=e.charCodeAt(0).toString(16);return`\\x${`00${t}`.substring(t.length)}`}return e}function Cl(e,t){let n="";switch(e){case Ja.Char:n=void 0===t?`JSON5: invalid end of input at ${tl}:${nl}`:`JSON5: invalid character '${yl(t)}' at ${tl}:${nl}`;break;case Ja.EOF:n=`JSON5: invalid end of input at ${tl}:${nl}`;break;case Ja.Identifier:nl-=5,n=`JSON5: invalid identifier character at ${tl}:${nl}`}const r=new Fl(n);return r.lineNumber=tl,r.columnNumber=nl,r}class Fl extends SyntaxError{}var gl={},Al={},vl={},Sl=p&&p.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(vl,"__esModule",{value:!0}),vl.getHvigorUserHomeCacheDir=void 0;const wl=Sl(qn),Ol=Sl(t),_l=Sl(e),bl=h;vl.getHvigorUserHomeCacheDir=function(){const e=_l.default.resolve(Ol.default.homedir(),bl.HVIGOR_USER_HOME_DIR_NAME),t=process.env.HVIGOR_USER_HOME;return void 0!==t&&_l.default.isAbsolute(t)?(wl.default.ensureDirSync(t),t):e},function(t){var n=p&&p.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.HVIGOR_PROJECT_WRAPPER_HOME=t.HVIGOR_PROJECT_ROOT_DIR=t.HVIGOR_PROJECT_CACHES_HOME=t.HVIGOR_PNPM_STORE_PATH=t.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH=t.HVIGOR_WRAPPER_TOOLS_HOME=t.HVIGOR_USER_HOME=void 0;const r=n(e),u=vl,o=h;t.HVIGOR_USER_HOME=(0,u.getHvigorUserHomeCacheDir)(),t.HVIGOR_WRAPPER_TOOLS_HOME=r.default.resolve(t.HVIGOR_USER_HOME,"wrapper","tools"),t.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH=r.default.resolve(t.HVIGOR_WRAPPER_TOOLS_HOME,"node_modules",".bin",o.PNPM_TOOL),t.HVIGOR_PNPM_STORE_PATH=r.default.resolve(t.HVIGOR_USER_HOME,"caches"),t.HVIGOR_PROJECT_CACHES_HOME=r.default.resolve(t.HVIGOR_USER_HOME,o.PROJECT_CACHES),t.HVIGOR_PROJECT_ROOT_DIR=process.cwd(),t.HVIGOR_PROJECT_WRAPPER_HOME=r.default.resolve(t.HVIGOR_PROJECT_ROOT_DIR,o.HVIGOR)}(Al);var Bl={},Pl=p&&p.__createBinding||(Object.create?function(e,t,n,r){void 0===r&&(r=n);var u=Object.getOwnPropertyDescriptor(t,n);u&&!("get"in u?!t.__esModule:u.writable||u.configurable)||(u={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,u)}:function(e,t,n,r){void 0===r&&(r=n),e[r]=t[n]}),Il=p&&p.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),kl=p&&p.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.prototype.hasOwnProperty.call(e,n)&&Pl(t,e,n);return Il(t,e),t},xl=p&&p.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(Bl,"__esModule",{value:!0}),Bl.isFileExists=Bl.offlinePluginConversion=Bl.executeCommand=Bl.getNpmPath=Bl.hasNpmPackInPaths=void 0;const Nl=r,Rl=xl(n),Tl=kl(e),Ml=h,Ll=O;Bl.hasNpmPackInPaths=function(e,t){try{return require.resolve(e,{paths:[...t]}),!0}catch(e){return!1}},Bl.getNpmPath=function(){const e=process.execPath;return Tl.join(Tl.dirname(e),Ml.NPM_TOOL)},Bl.executeCommand=function(e,t,n){0!==(0,Nl.spawnSync)(e,t,n).status&&(0,Ll.logErrorAndExit)(`Error: ${e} ${t} execute failed.See above for details.`)},Bl.offlinePluginConversion=function(e,t){return t.startsWith("file:")||t.endsWith(".tgz")?Tl.resolve(e,Ml.HVIGOR,t.replace("file:","")):t},Bl.isFileExists=function(e){return Rl.default.existsSync(e)&&Rl.default.statSync(e).isFile()},function(u){var o=p&&p.__createBinding||(Object.create?function(e,t,n,r){void 0===r&&(r=n);var u=Object.getOwnPropertyDescriptor(t,n);u&&!("get"in u?!t.__esModule:u.writable||u.configurable)||(u={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,u)}:function(e,t,n,r){void 0===r&&(r=n),e[r]=t[n]}),i=p&&p.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),s=p&&p.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.prototype.hasOwnProperty.call(e,n)&&o(t,e,n);return i(t,e),t},c=p&&p.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(u,"__esModule",{value:!0}),u.executeInstallPnpm=u.isPnpmInstalled=u.environmentHandler=u.checkNpmConifg=u.PNPM_VERSION=void 0;const a=r,l=s(n),f=c(t),d=s(e),D=h,E=Al,m=Al,y=O,C=Bl;u.PNPM_VERSION="7.30.0",u.checkNpmConifg=function(){const e=d.resolve(E.HVIGOR_PROJECT_ROOT_DIR,".npmrc"),t=d.resolve(f.default.homedir(),".npmrc");if((0,C.isFileExists)(e)||(0,C.isFileExists)(t))return;const n=(0,C.getNpmPath)(),r=(0,a.spawnSync)(n,["config","get","prefix"],{cwd:E.HVIGOR_PROJECT_ROOT_DIR});if(0!==r.status||!r.stdout)return void(0,y.logErrorAndExit)("Error: The hvigor depends on the npmrc file. Configure the npmrc file first.");const u=d.resolve(`${r.stdout}`.replace(/[\r\n]/gi,""),".npmrc");(0,C.isFileExists)(u)||(0,y.logErrorAndExit)("Error: The hvigor depends on the npmrc file. Configure the npmrc file first.")},u.environmentHandler=function(){process.env["npm_config_update-notifier"]="false"},u.isPnpmInstalled=function(){return!!l.existsSync(m.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH)&&(0,C.hasNpmPackInPaths)("pnpm",[m.HVIGOR_WRAPPER_TOOLS_HOME])},u.executeInstallPnpm=function(){(0,y.logInfo)(`Installing pnpm@${u.PNPM_VERSION}...`);const e=(0,C.getNpmPath)();!function(){const e=d.resolve(m.HVIGOR_WRAPPER_TOOLS_HOME,D.DEFAULT_PACKAGE_JSON);try{l.existsSync(m.HVIGOR_WRAPPER_TOOLS_HOME)||l.mkdirSync(m.HVIGOR_WRAPPER_TOOLS_HOME,{recursive:!0});const t={dependencies:{}};t.dependencies[D.PNPM]=u.PNPM_VERSION,l.writeFileSync(e,JSON.stringify(t))}catch(t){(0,y.logErrorAndExit)(`Error: EPERM: operation not permitted,create ${e} failed.`)}}(),(0,C.executeCommand)(e,["install","pnpm"],{cwd:m.HVIGOR_WRAPPER_TOOLS_HOME,stdio:["inherit","inherit","inherit"],env:process.env}),(0,y.logInfo)("Pnpm install success.")}}(gl);var jl={},$l={},Hl=p&&p.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty($l,"__esModule",{value:!0}),$l.getHvigorUserHomeCacheDir=void 0;const Gl=Hl(qn),Vl=Hl(t),Ul=Hl(e),Jl=h,Wl=O;let zl=!1;$l.getHvigorUserHomeCacheDir=function(){const e=Ul.default.resolve(Vl.default.homedir(),Jl.HVIGOR_USER_HOME_DIR_NAME),t=process.env.HVIGOR_USER_HOME;return void 0===t?e:Ul.default.isAbsolute(t)?Gl.default.existsSync(t)&&Gl.default.statSync(t).isFile()?((0,Wl.logInfo)(`File already exists: ${t}`),e):(Gl.default.ensureDirSync(t),t):(zl||((0,Wl.logInfo)(`Invalid custom userhome hvigor data dir:${t}`),zl=!0),e)},function(t){var n=p&&p.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.HVIGOR_PROJECT_WRAPPER_HOME=t.HVIGOR_PROJECT_ROOT_DIR=t.HVIGOR_PNPM_STORE_PATH=t.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH=t.HVIGOR_WRAPPER_TOOLS_HOME=t.HVIGOR_USER_HOME=void 0;const r=n(e),u=h,o=$l;t.HVIGOR_USER_HOME=(0,o.getHvigorUserHomeCacheDir)(),t.HVIGOR_WRAPPER_TOOLS_HOME=r.default.resolve(t.HVIGOR_USER_HOME,"wrapper","tools"),t.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH=r.default.resolve(t.HVIGOR_WRAPPER_TOOLS_HOME,"node_modules",".bin",u.PNPM_TOOL),t.HVIGOR_PNPM_STORE_PATH=r.default.resolve(t.HVIGOR_USER_HOME,"caches"),t.HVIGOR_PROJECT_ROOT_DIR=process.cwd(),t.HVIGOR_PROJECT_WRAPPER_HOME=r.default.resolve(t.HVIGOR_PROJECT_ROOT_DIR,u.HVIGOR)}(jl);var Kl=p&&p.__createBinding||(Object.create?function(e,t,n,r){void 0===r&&(r=n);var u=Object.getOwnPropertyDescriptor(t,n);u&&!("get"in u?!t.__esModule:u.writable||u.configurable)||(u={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,u)}:function(e,t,n,r){void 0===r&&(r=n),e[r]=t[n]}),ql=p&&p.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),Yl=p&&p.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.prototype.hasOwnProperty.call(e,n)&&Kl(t,e,n);return ql(t,e),t},Zl=p&&p.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(R,"__esModule",{value:!0});var Xl=R.initProjectWorkSpace=void 0;const Ql=Yl(n),ef=Zl(qn),tf=Zl(t),nf=Yl(e),rf=Zl(c),uf=Yn,of=h,sf=xa,cf=Na,af=Ra,lf=O,ff=gl,df=Bl,Df=jl;let pf,hf,Ef;function mf(e,t,n){return void 0!==n.dependencies&&(0,df.offlinePluginConversion)(Df.HVIGOR_PROJECT_ROOT_DIR,t.dependencies[e])===nf.normalize(n.dependencies[e])}Xl=R.initProjectWorkSpace=function(){if(pf=function(){const e=nf.resolve(Df.HVIGOR_PROJECT_WRAPPER_HOME,of.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME);Ql.existsSync(e)||(0,lf.logErrorAndExit)(`Error: Hvigor config file ${e} does not exist.`);return(0,af.parseJsonFile)(e)}(),Ef=function(e){let t;t=!(0,sf.isCI)()&&function(e){const t=e.hvigorVersion;if(t.startsWith("file:")||t.endsWith(".tgz"))return!1;const n=e.dependencies,r=Object.getOwnPropertyNames(n);for(const e of r){const t=n[e];if(t.startsWith("file:")||t.endsWith(".tgz"))return!1}if(1===r.length&&"@ohos/hvigor-ohos-plugin"===r[0])return t>"2.5.0";return!1}(e)?function(e){let t=`${of.HVIGOR_ENGINE_PACKAGE_NAME}@${e.hvigorVersion}`;const n=e.dependencies;if(n){Object.getOwnPropertyNames(n).sort().forEach((e=>{t+=`,${e}@${n[e]}`}))}return(0,cf.hash)(t)}(e):(0,cf.hash)(rf.default.cwd());return nf.resolve(Df.HVIGOR_USER_HOME,"project_caches",t)}(pf),hf=function(){const e=nf.resolve(Ef,of.WORK_SPACE,of.DEFAULT_PACKAGE_JSON);return Ql.existsSync(e)?(0,af.parseJsonFile)(e):{dependencies:{}}}(),function(){const e=nf.resolve(Df.HVIGOR_USER_HOME,of.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME);if(Ql.existsSync(e))(0,af.parseJsonFile)(e)}(),!(0,df.hasNpmPackInPaths)(of.HVIGOR_ENGINE_PACKAGE_NAME,[nf.join(Ef,of.WORK_SPACE)])||(0,df.offlinePluginConversion)(Df.HVIGOR_PROJECT_ROOT_DIR,pf.hvigorVersion)!==hf.dependencies[of.HVIGOR_ENGINE_PACKAGE_NAME]||!function(){function e(e){const t=null==e?void 0:e.dependencies;return void 0===t?0:Object.getOwnPropertyNames(t).length}const t=e(pf),n=e(hf);if(t+1!==n)return!1;for(const e in null==pf?void 0:pf.dependencies)if(!(0,df.hasNpmPackInPaths)(e,[nf.join(Ef,of.WORK_SPACE)])||!mf(e,pf,hf))return!1;return!0}())try{const e=rf.default.hrtime();(0,ff.checkNpmConifg)(),function(){(0,lf.logInfo)("Hvigor installing...");for(const e in pf.dependencies)pf.dependencies[e]&&(pf.dependencies[e]=(0,df.offlinePluginConversion)(Df.HVIGOR_PROJECT_ROOT_DIR,pf.dependencies[e]));const e={dependencies:{...pf.dependencies}};e.dependencies[of.HVIGOR_ENGINE_PACKAGE_NAME]=(0,df.offlinePluginConversion)(Df.HVIGOR_PROJECT_ROOT_DIR,pf.hvigorVersion);const t=nf.join(Ef,of.WORK_SPACE);try{Ql.mkdirSync(t,{recursive:!0});const n=nf.resolve(t,of.DEFAULT_PACKAGE_JSON);Ql.writeFileSync(n,JSON.stringify(e))}catch(e){(0,lf.logErrorAndExit)(e)}(function(){const e=["config","set","store-dir",Df.HVIGOR_PNPM_STORE_PATH],t={cwd:nf.join(Ef,of.WORK_SPACE),stdio:["inherit","inherit","inherit"]};(0,df.executeCommand)(Df.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH,e,t)})(),function(){const e=["install"];(0,sf.isCI)()&&e.push("--no-frozen-lockfile");ef.default.existsSync(nf.resolve(Df.HVIGOR_PROJECT_ROOT_DIR,".npmrc"))&&(rf.default.env.npm_config_userconfig=function(e){const t=nf.resolve(Df.HVIGOR_USER_HOME,"project_caches",(0,cf.hash)(rf.default.cwd()),".npmrc");try{const n=ef.default.readFileSync(nf.resolve(tf.default.homedir(),".npmrc"),"utf-8"),r=`${n}\n${ef.default.readFileSync(e,"utf-8")}`;ef.default.ensureFileSync(t),ef.default.writeFileSync(t,r)}catch(e){(0,lf.logErrorAndExit)(e)}return t}(nf.resolve(Df.HVIGOR_PROJECT_ROOT_DIR,".npmrc")));const t={cwd:nf.join(Ef,of.WORK_SPACE),stdio:["inherit","inherit","inherit"],env:rf.default.env};(0,df.executeCommand)(Df.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH,e,t)}(),(0,lf.logInfo)("Hvigor install success.")}();const t=rf.default.hrtime(e);uf.hvigorTrace.HVIGOR_INSTALL_TIME=1e9*t[0]+t[1]}catch(e){!function(){const e=nf.join(Ef,of.WORK_SPACE);if((0,lf.logInfo)("Hvigor cleaning..."),!Ql.existsSync(e))return;const t=Ql.readdirSync(e);if(!t||0===t.length)return;const n=nf.resolve(Ef,"node_modules","@ohos","hvigor","bin","hvigor.js");Ql.existsSync(n)&&(0,df.executeCommand)(rf.default.argv[0],[n,"--stop-daemon"],{});try{t.forEach((t=>{Ql.rmSync(nf.resolve(e,t),{recursive:!0})}))}catch(t){(0,lf.logErrorAndExit)(`The hvigor build tool cannot be installed. Please manually clear the workspace directory and synchronize the project again.\n\n Workspace Path: ${e}.`)}}()}return Ef},function(){gl.environmentHandler(),gl.isPnpmInstalled()||(gl.checkNpmConifg(),gl.executeInstallPnpm());const t=Xl();P(e.join(t,S))}(); \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/hvigorfile.ts b/shell/platform/ohos/flutter_embedding/hvigorfile.ts new file mode 100755 index 0000000000000000000000000000000000000000..8f2d2aafe6d6a3a71a9944ebd0c91fbc308ac9d1 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/hvigorfile.ts @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/hvigorw b/shell/platform/ohos/flutter_embedding/hvigorw new file mode 100755 index 0000000000000000000000000000000000000000..54aadd226b453397860013d328fd01031648fc31 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/hvigorw @@ -0,0 +1,48 @@ +#!/bin/bash + +# ---------------------------------------------------------------------------- +# Hvigor startup script, version 1.0.0 +# +# Required ENV vars: +# ------------------ +# NODE_HOME - location of a Node home dir +# or +# Add /usr/local/nodejs/bin to the PATH environment variable +# ---------------------------------------------------------------------------- + +HVIGOR_APP_HOME=$(dirname $(readlink -f $0)) +HVIGOR_WRAPPER_SCRIPT=${HVIGOR_APP_HOME}/hvigor/hvigor-wrapper.js +warn() { + echo "" + echo -e "\033[1;33m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m" +} + +error() { + echo "" + echo -e "\033[1;31m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m" +} + +fail() { + error "$@" + exit 1 +} + +# Determine node to start hvigor wrapper script +if [ -n "${NODE_HOME}" ];then + EXECUTABLE_NODE="${NODE_HOME}/bin/node" + if [ ! -x "$EXECUTABLE_NODE" ];then + fail "ERROR: NODE_HOME is set to an invalid directory,check $NODE_HOME\n\nPlease set NODE_HOME in your environment to the location where your nodejs installed" + fi +else + EXECUTABLE_NODE="node" + which ${EXECUTABLE_NODE} > /dev/null 2>&1 || fail "ERROR: NODE_HOME is not set and not 'node' command found in your path" +fi + +# Check hvigor wrapper script +if [ ! -r "$HVIGOR_WRAPPER_SCRIPT" ];then + fail "ERROR: Couldn't find hvigor/hvigor-wrapper.js in ${HVIGOR_APP_HOME}" +fi + +# start hvigor-wrapper script +exec "${EXECUTABLE_NODE}" \ + "${HVIGOR_WRAPPER_SCRIPT}" "$@" diff --git a/shell/platform/ohos/flutter_embedding/hvigorw.bat b/shell/platform/ohos/flutter_embedding/hvigorw.bat new file mode 100755 index 0000000000000000000000000000000000000000..6861293e47dfd0186da380321b73be9033507e24 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/hvigorw.bat @@ -0,0 +1,64 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Hvigor startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +set WRAPPER_MODULE_PATH=%APP_HOME%\hvigor\hvigor-wrapper.js +set NODE_EXE=node.exe + +goto start + +:start +@rem Find node.exe +if defined NODE_HOME goto findNodeFromNodeHome + +%NODE_EXE% --version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH. +echo. +echo Please set the NODE_HOME variable in your environment to match the +echo location of your NodeJs installation. + +goto fail + +:findNodeFromNodeHome +set NODE_HOME=%NODE_HOME:"=% +set NODE_EXE_PATH=%NODE_HOME%/%NODE_EXE% + +if exist "%NODE_EXE_PATH%" goto execute +echo. +echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH. +echo. +echo Please set the NODE_HOME variable in your environment to match the +echo location of your NodeJs installation. + +goto fail + +:execute +@rem Execute hvigor +"%NODE_EXE%" %WRAPPER_MODULE_PATH% %* + +if "%ERRORLEVEL%" == "0" goto hvigorwEnd + +:fail +exit /b 1 + +:hvigorwEnd +if "%OS%" == "Windows_NT" endlocal + +:end diff --git a/shell/platform/ohos/flutter_embedding/oh-package.json5 b/shell/platform/ohos/flutter_embedding/oh-package.json5 new file mode 100755 index 0000000000000000000000000000000000000000..6990a41a16d7d0cedbfca94edf2f197f6d33cdd5 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/oh-package.json5 @@ -0,0 +1,29 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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. +*/ + +{ + "modelVersion": "5.0.0", + "name": "config", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.6" + } +} diff --git a/shell/platform/ohos/image_lru.cpp b/shell/platform/ohos/image_lru.cpp new file mode 100644 index 0000000000000000000000000000000000000000..136b0e68c108df5e0e8d2174cacf72e7c948eef8 --- /dev/null +++ b/shell/platform/ohos/image_lru.cpp @@ -0,0 +1,70 @@ +// Copyright 2013 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. + +#include "flutter/shell/platform/ohos/image_lru.h" +#include "fml/logging.h" +namespace flutter { + +sk_sp ImageLRU::FindImage(NativeBufferKey key) { + if (key == 0) { + return nullptr; + } + for (size_t i = 0u; i < kMaxQueueSize; i++) { + if (images_[i].key == key) { + auto result = images_[i].value; + UpdateKey(result, key); + return result; + } + } + return nullptr; +} + +void ImageLRU::UpdateKey(const sk_sp& image, + NativeBufferKey key) { + if (images_[0].key == key) { + return; + } + size_t i = 1u; + for (; i < kMaxQueueSize; i++) { + if (images_[i].key == key) { + break; + } + } + for (auto j = i; j > 0; j--) { + images_[j] = images_[j - 1]; + } + images_[0] = Data{.key = key, .value = image}; +} + +NativeBufferKey ImageLRU::AddImage(const sk_sp& image, + NativeBufferKey key) { + NativeBufferKey lru_key = images_[kMaxQueueSize - 1].key; + size_t i = 0u; + for (; i < kMaxQueueSize; i++) { + if (images_[i].key == key) { + i++; + // needn't delete image + lru_key = 0; + break; + } + } + // Note: i <= kMaxQueueSize + i--; + for (; i > 0; i--) { + images_[i] = images_[i - 1]; + } + images_[0] = Data{.key = key, .value = image}; + if (lru_key != 0) { + FML_LOG(INFO) << "lru release one " << lru_key; + } + return lru_key; +} + +void ImageLRU::Clear() { + for (size_t i = 0u; i < kMaxQueueSize; i++) { + images_[i] = Data{.key = 0u, .value = nullptr}; + } +} + +} // namespace flutter diff --git a/shell/platform/ohos/image_lru.h b/shell/platform/ohos/image_lru.h new file mode 100644 index 0000000000000000000000000000000000000000..4dfe13bf27fb0d22289f22a3460e2e262e4f0f82 --- /dev/null +++ b/shell/platform/ohos/image_lru.h @@ -0,0 +1,59 @@ +// Copyright 2013 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. + +#ifndef FLUTTER_SHELL_PLATFORM_OHOS_IMAGE_LRU_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_IMAGE_LRU_H_ + +#include +#include + +#include "display_list/image/dl_image.h" + +namespace flutter { + +// This value needs to be larger than the number of swapchain images +// that a typical image reader will produce to ensure that we effectively +// cache. If the value is too small, we will unnecessarily churn through +// images, while if it is too large we may retain images longer than +// necessary. +// OHOS' camera queue size is 8 +// OHOS' video queue size is 9 +static constexpr size_t kMaxQueueSize = 9u; + +using NativeBufferKey = uint64_t; + +class ImageLRU { + public: + ImageLRU() = default; + + ~ImageLRU() = default; + + /// @brief Retrieve the image associated with the given [key], or nullptr. + sk_sp FindImage(NativeBufferKey key); + + /// @brief Add a new image to the cache with a key, returning the key of the + /// LRU entry that was removed. + /// + /// The value may be `0`, in which case nothing was removed. + NativeBufferKey AddImage(const sk_sp& image, + NativeBufferKey key); + + /// @brief Remove all entires from the image cache. + void Clear(); + + private: + /// @brief Marks [key] as the most recently used. + void UpdateKey(const sk_sp& image, NativeBufferKey key); + + struct Data { + NativeBufferKey key = 0u; + sk_sp value; + }; + + std::array images_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_IMAGE_LRU_H_ diff --git a/shell/platform/ohos/library_loader.cpp b/shell/platform/ohos/library_loader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6f84b16c23f4084c5e514738b95d3e4d5f19f807 --- /dev/null +++ b/shell/platform/ohos/library_loader.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/napi/platform_view_ohos_napi.h" +#include "flutter/shell/platform/ohos/ohos_main.h" +#include "napi/native_api.h" +#include "napi_common.h" +#include "ohos_logging.h" +#include "ohos_xcomponent_adapter.h" + +// namespace flutter { + +EXTERN_C_START +static napi_value Init(napi_env env, napi_value exports) { + FML_DLOG(INFO) << "Init NAPI Start."; + napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("nativeInit", flutter::OhosMain::NativeInit), + DECLARE_NAPI_FUNCTION( + "nativeUpdateRefreshRate", + flutter::PlatformViewOHOSNapi::nativeUpdateRefreshRate), + DECLARE_NAPI_FUNCTION("nativeUpdateSize", + flutter::PlatformViewOHOSNapi::nativeUpdateSize), + DECLARE_NAPI_FUNCTION("nativeUpdateDensity", + flutter::PlatformViewOHOSNapi::nativeUpdateDensity), + DECLARE_NAPI_FUNCTION( + "nativeRunBundleAndSnapshotFromLibrary", + flutter::PlatformViewOHOSNapi::nativeRunBundleAndSnapshotFromLibrary), + DECLARE_NAPI_FUNCTION( + "nativePrefetchDefaultFontManager", + flutter::PlatformViewOHOSNapi::nativePrefetchDefaultFontManager), + DECLARE_NAPI_FUNCTION( + "nativeCheckAndReloadFont", + flutter::PlatformViewOHOSNapi::nativeCheckAndReloadFont), + DECLARE_NAPI_FUNCTION( + "nativeGetIsSoftwareRenderingEnabled", + flutter::PlatformViewOHOSNapi::nativeGetIsSoftwareRenderingEnabled), + DECLARE_NAPI_FUNCTION("nativeAttach", + flutter::PlatformViewOHOSNapi::nativeAttach), + DECLARE_NAPI_FUNCTION("nativeSpawn", + flutter::PlatformViewOHOSNapi::nativeSpawn), + DECLARE_NAPI_FUNCTION("nativeDestroy", + flutter::PlatformViewOHOSNapi::nativeDestroy), + DECLARE_NAPI_FUNCTION( + "nativeSetViewportMetrics", + flutter::PlatformViewOHOSNapi::nativeSetViewportMetrics), + DECLARE_NAPI_FUNCTION( + "nativeSetAccessibilityFeatures", + flutter::PlatformViewOHOSNapi::nativeSetAccessibilityFeatures), + DECLARE_NAPI_FUNCTION( + "nativeCleanupMessageData", + flutter::PlatformViewOHOSNapi::nativeCleanupMessageData), + DECLARE_NAPI_FUNCTION( + "nativeDispatchEmptyPlatformMessage", + flutter::PlatformViewOHOSNapi::nativeDispatchEmptyPlatformMessage), + DECLARE_NAPI_FUNCTION( + "nativeDispatchPlatformMessage", + flutter::PlatformViewOHOSNapi::nativeDispatchPlatformMessage), + DECLARE_NAPI_FUNCTION( + "nativeInvokePlatformMessageEmptyResponseCallback", + flutter::PlatformViewOHOSNapi:: + nativeInvokePlatformMessageEmptyResponseCallback), + DECLARE_NAPI_FUNCTION("nativeInvokePlatformMessageResponseCallback", + flutter::PlatformViewOHOSNapi:: + nativeInvokePlatformMessageResponseCallback), + DECLARE_NAPI_FUNCTION( + "nativeLoadDartDeferredLibrary", + flutter::PlatformViewOHOSNapi::nativeLoadDartDeferredLibrary), + DECLARE_NAPI_FUNCTION( + "nativeUpdateOhosAssetManager", + flutter::PlatformViewOHOSNapi::nativeUpdateOhosAssetManager), + DECLARE_NAPI_FUNCTION( + "nativeDeferredComponentInstallFailure", + flutter::PlatformViewOHOSNapi::nativeDeferredComponentInstallFailure), + DECLARE_NAPI_FUNCTION("nativeGetPixelMap", + flutter::PlatformViewOHOSNapi::nativeGetPixelMap), + DECLARE_NAPI_FUNCTION( + "nativeNotifyLowMemoryWarning", + flutter::PlatformViewOHOSNapi::nativeNotifyLowMemoryWarning), + DECLARE_NAPI_FUNCTION( + "nativeFlutterTextUtilsIsEmoji", + flutter::PlatformViewOHOSNapi::nativeFlutterTextUtilsIsEmoji), + DECLARE_NAPI_FUNCTION( + "nativeFlutterTextUtilsIsEmojiModifier", + flutter::PlatformViewOHOSNapi::nativeFlutterTextUtilsIsEmojiModifier), + DECLARE_NAPI_FUNCTION("nativeFlutterTextUtilsIsEmojiModifierBase", + flutter::PlatformViewOHOSNapi:: + nativeFlutterTextUtilsIsEmojiModifierBase), + DECLARE_NAPI_FUNCTION("nativeFlutterTextUtilsIsVariationSelector", + flutter::PlatformViewOHOSNapi:: + nativeFlutterTextUtilsIsVariationSelector), + DECLARE_NAPI_FUNCTION("nativeFlutterTextUtilsIsRegionalIndicator", + flutter::PlatformViewOHOSNapi:: + nativeFlutterTextUtilsIsRegionalIndicator), + DECLARE_NAPI_FUNCTION( + "nativeGetSystemLanguages", + flutter::PlatformViewOHOSNapi::nativeGetSystemLanguages), + DECLARE_NAPI_FUNCTION( + "nativeXComponentAttachFlutterEngine", + flutter::PlatformViewOHOSNapi::nativeXComponentAttachFlutterEngine), + DECLARE_NAPI_FUNCTION( + "nativeXComponentPreDraw", + flutter::PlatformViewOHOSNapi::nativeXComponentPreDraw), + DECLARE_NAPI_FUNCTION( + "nativeXComponentDetachFlutterEngine", + flutter::PlatformViewOHOSNapi::nativeXComponentDetachFlutterEngine), + DECLARE_NAPI_FUNCTION( + "nativeXComponentDispatchMouseWheel", + flutter::PlatformViewOHOSNapi::nativeXComponentDispatchMouseWheel), + DECLARE_NAPI_FUNCTION( + "nativeRegisterTexture", + flutter::PlatformViewOHOSNapi::nativeRegisterTexture), + DECLARE_NAPI_FUNCTION( + "nativeSetTextureBufferSize", + flutter::PlatformViewOHOSNapi::nativeSetTextureBufferSize), + DECLARE_NAPI_FUNCTION( + "nativeNotifyTextureResizing", + flutter::PlatformViewOHOSNapi::nativeNotifyTextureResizing), + DECLARE_NAPI_FUNCTION( + "nativeUnregisterTexture", + flutter::PlatformViewOHOSNapi::nativeUnregisterTexture), + DECLARE_NAPI_FUNCTION( + "nativeGetTextureWindowId", + flutter::PlatformViewOHOSNapi::nativeGetTextureWindowId), + DECLARE_NAPI_FUNCTION( + "nativeSetExternalNativeImage", + flutter::PlatformViewOHOSNapi::nativeSetExternalNativeImage), + DECLARE_NAPI_FUNCTION( + "nativeResetExternalTexture", + flutter::PlatformViewOHOSNapi::nativeResetExternalTexture), + DECLARE_NAPI_FUNCTION( + "nativeMarkTextureFrameAvailable", + flutter::PlatformViewOHOSNapi::nativeMarkTextureFrameAvailable), + DECLARE_NAPI_FUNCTION( + "nativeRegisterPixelMap", + flutter::PlatformViewOHOSNapi::nativeRegisterPixelMap), + DECLARE_NAPI_FUNCTION("nativeEncodeUtf8", + flutter::PlatformViewOHOSNapi::nativeEncodeUtf8), + DECLARE_NAPI_FUNCTION("nativeDecodeUtf8", + flutter::PlatformViewOHOSNapi::nativeDecodeUtf8), + DECLARE_NAPI_FUNCTION( + "nativeSetTextureBackGroundPixelMap", + flutter::PlatformViewOHOSNapi::nativeSetTextureBackGroundPixelMap), + DECLARE_NAPI_FUNCTION( + "nativeSetFontWeightScale", + flutter::PlatformViewOHOSNapi::nativeSetFontWeightScale), + + DECLARE_NAPI_FUNCTION( + "nativeLookupCallbackInformation", + flutter::PlatformViewOHOSNapi::nativeLookupCallbackInformation), + DECLARE_NAPI_FUNCTION( + "nativeUnicodeIsEmoji", + flutter::PlatformViewOHOSNapi::nativeUnicodeIsEmoji), + DECLARE_NAPI_FUNCTION( + "nativeUnicodeIsEmojiModifier", + flutter::PlatformViewOHOSNapi::nativeUnicodeIsEmojiModifier), + DECLARE_NAPI_FUNCTION( + "nativeUnicodeIsEmojiModifierBase", + flutter::PlatformViewOHOSNapi::nativeUnicodeIsEmojiModifierBase), + DECLARE_NAPI_FUNCTION( + "nativeUnicodeIsVariationSelector", + flutter::PlatformViewOHOSNapi::nativeUnicodeIsVariationSelector), + DECLARE_NAPI_FUNCTION("nativeUnicodeIsRegionalIndicatorSymbol", + flutter::PlatformViewOHOSNapi:: + nativeUnicodeIsRegionalIndicatorSymbol), + DECLARE_NAPI_FUNCTION( + "nativeAccessibilityStateChange", + flutter::PlatformViewOHOSNapi::nativeAccessibilityStateChange), + DECLARE_NAPI_FUNCTION( + "nativeAccessibilityAnnounce", + flutter::PlatformViewOHOSNapi::nativeAccessibilityAnnounce), + DECLARE_NAPI_FUNCTION( + "nativeAccessibilityOnTap", + flutter::PlatformViewOHOSNapi::nativeAccessibilityOnTap), + DECLARE_NAPI_FUNCTION( + "nativeAccessibilityOnLongPress", + flutter::PlatformViewOHOSNapi::nativeAccessibilityOnLongPress), + DECLARE_NAPI_FUNCTION( + "nativeAccessibilityOnTooltip", + flutter::PlatformViewOHOSNapi::nativeAccessibilityOnTooltip), + DECLARE_NAPI_FUNCTION( + "nativeSetSemanticsEnabled", + flutter::PlatformViewOHOSNapi::nativeSetSemanticsEnabled), + DECLARE_NAPI_FUNCTION( + "nativeSetFontWeightScale", + flutter::PlatformViewOHOSNapi::nativeSetFontWeightScale), + DECLARE_NAPI_FUNCTION( + "nativeSetFlutterNavigationAction", + flutter::PlatformViewOHOSNapi::nativeSetFlutterNavigationAction), + DECLARE_NAPI_FUNCTION( + "nativeEnableFrameCache", + flutter::PlatformViewOHOSNapi::nativeEnableFrameCache), + DECLARE_NAPI_FUNCTION( + "nativeUpdateCurrentXComponentId", + flutter::PlatformViewOHOSNapi::nativeUpdateCurrentXComponentId), + }; + + FML_DLOG(INFO) << "Init NAPI size=" << sizeof(desc) / sizeof(desc[0]); + napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); + bool ret = flutter::XComponentAdapter::GetInstance()->Export(env, exports); + if (!ret) { + FML_DLOG(ERROR) << "Init NAPI Failed."; + } else { + FML_DLOG(INFO) << "Init NAPI Succeed."; + } + return exports; +} +EXTERN_C_END + +static napi_module flutterModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "flutter", + .nm_priv = ((void*)0), + .reserved = {0}, +}; + +extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { + napi_module_register(&flutterModule); +} + +//} diff --git a/shell/platform/ohos/napi/platform_view_ohos_napi.cpp b/shell/platform/ohos/napi/platform_view_ohos_napi.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6176c33e73494c792ee9dcb7dae3b5d852fc0270 --- /dev/null +++ b/shell/platform/ohos/napi/platform_view_ohos_napi.cpp @@ -0,0 +1,2491 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "platform_view_ohos_napi.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "flutter/common/constants.h" +#include "flutter/fml/make_copyable.h" +#include "flutter/fml/platform/ohos/napi_util.h" +#include "flutter/lib/ui/plugins/callback_cache.h" +#include "flutter/shell/platform/ohos/ohos_logging.h" +#include "flutter/shell/platform/ohos/ohos_main.h" +#include "flutter/shell/platform/ohos/ohos_shell_holder.h" +#include "flutter/shell/platform/ohos/ohos_xcomponent_adapter.h" +#include "flutter/shell/platform/ohos/surface/ohos_native_window.h" +#include "flutter/shell/platform/ohos/types.h" +#include "unicode/uchar.h" + +#define OHOS_SHELL_HOLDER (reinterpret_cast(shell_holder)) +namespace flutter { + +int64_t PlatformViewOHOSNapi::display_width = 0; +int64_t PlatformViewOHOSNapi::display_height = 0; +int64_t PlatformViewOHOSNapi::display_refresh_rate = 60; +double PlatformViewOHOSNapi::display_density_pixels = 1.0; + +napi_env PlatformViewOHOSNapi::env_; +std::vector PlatformViewOHOSNapi::system_languages; + +/** + * @brief send empty PlatformMessage + * @note + * @param nativeShellHolderId: number,channel: string,responseId: number + * @return void + */ +napi_value PlatformViewOHOSNapi::nativeDispatchEmptyPlatformMessage( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeDispatchEmptyPlatformMessage"; + napi_status ret; + size_t argc = 3; + napi_value args[3] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + int64_t shell_holder, responseId; + std::string channel; + ret = napi_get_value_int64(env, args[0], &shell_holder); + if (ret != napi_ok) { + LOGE("nativeDispatchEmptyPlatformMessage napi get shell_holder error"); + return nullptr; + } + fml::napi::GetString(env, args[1], channel); + FML_DLOG(INFO) << "nativeDispatchEmptyPlatformMessage channel:" << channel; + ret = napi_get_value_int64(env, args[2], &responseId); + if (ret != napi_ok) { + LOGE("nativeDispatchEmptyPlatformMessage napi get responseId error"); + return nullptr; + } + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeDispatchEmptyPlatformMessage " + "DispatchEmptyPlatformMessage"; + OHOS_SHELL_HOLDER->GetPlatformView()->DispatchEmptyPlatformMessage( + channel, responseId); + return nullptr; +} + +/** + * @brief send PlatformMessage + * @note + * @param nativeShellHolderId: number,channel: string,message: + * ArrayBuffer,position: number,responseId: number + * @return void + */ +napi_value PlatformViewOHOSNapi::nativeDispatchPlatformMessage( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeDispatchPlatformMessage"; + napi_status ret; + napi_value thisArg; + size_t argc = 5; + napi_value args[5] = {nullptr}; + int64_t shell_holder, responseId, position; + std::string channel; + void* message = nullptr; + size_t message_lenth = 0; + + int32_t status; + + ret = napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr); + if (argc < 5 || ret != napi_ok) { + FML_DLOG(ERROR) << "nativeDispatchPlatformMessage napi get argc ,argc=" + << argc << "<5,error:" << ret; + napi_throw_type_error(env, nullptr, "Wrong number of arguments"); + return nullptr; + } + napi_value napiShellHolder = args[0]; + napi_value napiChannel = args[1]; + napi_value napiMessage = args[2]; + napi_value napiPos = args[3]; + napi_value napiResponseId = args[4]; + + ret = napi_get_value_int64(env, napiShellHolder, &shell_holder); + if (ret != napi_ok) { + LOGE("nativeDispatchPlatformMessage napi get shell_holder error"); + return nullptr; + } + FML_DLOG(INFO) << "nativeDispatchPlatformMessage:shell_holder:" + << shell_holder; + + if (0 != (status = fml::napi::GetString(env, napiChannel, channel))) { + FML_DLOG(ERROR) << "nativeDispatchPlatformMessage napi get channel error:" + << status; + return nullptr; + } + FML_DLOG(INFO) << "nativeDispatchEmptyPlatformMessage channel:" << channel; + + if (0 != (status = fml::napi::GetArrayBuffer(env, napiMessage, &message, + &message_lenth))) { + FML_DLOG(ERROR) << "nativeDispatchPlatformMessage napi get message error:" + << status; + return nullptr; + } + if (message == nullptr) { + FML_LOG(ERROR) + << "nativeInvokePlatformMessageResponseCallback message null"; + return nullptr; + } + ret = napi_get_value_int64(env, napiPos, &position); + if (ret != napi_ok) { + LOGE("nativeDispatchPlatformMessage napi get position error"); + return nullptr; + } + ret = napi_get_value_int64(env, napiResponseId, &responseId); + if (ret != napi_ok) { + LOGE("nativeDispatchPlatformMessage napi get responseId error"); + return nullptr; + } + FML_DLOG(INFO) << "DispatchPlatformMessage,channel:" << channel + << ",message:" << message << ",position:" << position + << ",responseId:" << responseId; + + OHOS_SHELL_HOLDER->GetPlatformView()->DispatchPlatformMessage( + channel, message, position, responseId); + return nullptr; +} +/** + * @brief + * @note + * @param nativeShellHolderId: number,responseId: number + * @return void + */ +napi_value +PlatformViewOHOSNapi::nativeInvokePlatformMessageEmptyResponseCallback( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "nativeInvokePlatformMessageEmptyResponseCallback"; + napi_status ret; + size_t argc = 2; + napi_value args[2] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + int64_t shell_holder, responseId; + ret = napi_get_value_int64(env, args[0], &shell_holder); + if (ret != napi_ok) { + LOGE( + "nativeInvokePlatformMessageEmptyResponseCallback napi get " + "shell_holder error"); + return nullptr; + } + ret = napi_get_value_int64(env, args[1], &responseId); + if (ret != napi_ok) { + LOGE(" napi get responseId error"); + return nullptr; + } + FML_DLOG(INFO) << "InvokePlatformMessageEmptyResponseCallback"; + OHOS_SHELL_HOLDER->GetPlatformMessageHandler() + ->InvokePlatformMessageEmptyResponseCallback(responseId); + return nullptr; +} +/** + * @brief + * @note + * @param nativeShellHolderId: number, responseId: number, message: + * ArrayBuffer,position: number + * @return void + */ +napi_value PlatformViewOHOSNapi::nativeInvokePlatformMessageResponseCallback( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "nativeInvokePlatformMessageResponseCallback"; + napi_status ret; + size_t argc = 4; + napi_value args[4] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + int64_t shell_holder, responseId, position; + void* message = nullptr; + size_t message_lenth = 0; + ret = napi_get_value_int64(env, args[0], &shell_holder); + if (ret != napi_ok) { + LOGE(" napi get shell_holdererror"); + return nullptr; + } + ret = napi_get_value_int64(env, args[1], &responseId); + if (ret != napi_ok) { + LOGE(" napi get responseId error"); + return nullptr; + } + int32_t result = + fml::napi::GetArrayBuffer(env, args[2], &message, &message_lenth); + if (result != 0) { + FML_DLOG(ERROR) + << "nativeInvokePlatformMessageResponseCallback GetArrayBuffer error " + << result; + } + if (message == nullptr) { + FML_LOG(ERROR) + << "nativeInvokePlatformMessageResponseCallback message null"; + return nullptr; + } + ret = napi_get_value_int64(env, args[3], &position); + if (ret != napi_ok) { + LOGE("nativeInvokePlatformMessageResponseCallback napi get position error"); + return nullptr; + } + + uint8_t* response_data = static_cast(message); + FML_DCHECK(response_data != nullptr); + auto mapping = std::make_unique( + fml::MallocMapping::Copy(response_data, response_data + position)); + FML_DLOG(INFO) << "InvokePlatformMessageResponseCallback"; + OHOS_SHELL_HOLDER->GetPlatformMessageHandler() + ->InvokePlatformMessageResponseCallback(responseId, std::move(mapping)); + return nullptr; +} + +/* void PlatformViewOHOSNapi::FlutterViewHandlePlatformMessageResponse( + int responseId, + std::unique_ptr data) { + +} + */ +PlatformViewOHOSNapi::PlatformViewOHOSNapi(napi_env env) {} + +void PlatformViewOHOSNapi::FlutterViewHandlePlatformMessageResponse( + int reponse_id, + std::unique_ptr data) { + FML_DLOG(INFO) << "FlutterViewHandlePlatformMessageResponse"; + napi_status status; + napi_value callbackParam[2]; + status = napi_create_int64(env_, reponse_id, callbackParam); + if (status != napi_ok) { + FML_DLOG(ERROR) << "napi_create_int64 reponse_id fail"; + } + + if (data == nullptr) { + callbackParam[1] = NULL; + } else { + callbackParam[1] = fml::napi::CreateArrayBuffer( + env_, (void*)data->GetMapping(), data->GetSize()); + } + + status = fml::napi::InvokeJsMethod( + env_, ref_napi_obj_, "handlePlatformMessageResponse", 2, callbackParam); + if (status != napi_ok) { + FML_DLOG(ERROR) << "InvokeJsMethod fail "; + } +} + +void PlatformViewOHOSNapi::FlutterViewHandlePlatformMessage( + int reponse_id, + std::unique_ptr message) { + FML_DLOG(INFO) << "FlutterViewHandlePlatformMessage message channal " + << message->channel().c_str(); + + napi_value callbackParam[4]; + napi_status status; + + status = napi_create_string_utf8(env_, message->channel().c_str(), + message->channel().size(), callbackParam); + if (status != napi_ok) { + FML_DLOG(ERROR) << "napi_create_string_utf8 err " << status; + return; + } + + callbackParam[1] = fml::napi::CreateArrayBuffer( + env_, (void*)message->data().GetMapping(), message->data().GetSize()); + + status = napi_create_int64(env_, reponse_id, &callbackParam[2]); + if (status != napi_ok) { + FML_DLOG(ERROR) << "napi_create_int64 err " << status; + return; + } + if (message->hasData()) { + fml::MallocMapping mapping = message->releaseData(); + char* mapData = (char*)mapping.Release(); + mapData[mapping.GetSize()] = '\0'; + status = napi_create_string_utf8(env_, mapData, strlen(mapData), + &callbackParam[3]); + if (status != napi_ok) { + FML_DLOG(ERROR) << "napi_create_string_utf8 err " << status; + return; + } + FML_DLOG(INFO) << "FlutterViewHandlePlatformMessage mapData= " << mapData; + if (mapData) { + delete mapData; + } + } else { + callbackParam[3] = nullptr; + } + + status = fml::napi::InvokeJsMethod(env_, ref_napi_obj_, + "handlePlatformMessage", 4, callbackParam); + if (status != napi_ok) { + FML_DLOG(ERROR) << "InvokeJsMethod fail "; + } +} + +void PlatformViewOHOSNapi::FlutterViewOnFirstFrame(bool is_preload) { + FML_DLOG(INFO) << "FlutterViewOnFirstFrame"; + napi_value callbackParam[1]; + napi_status status = napi_create_int64(env_, is_preload, callbackParam); + if (status != napi_ok) { + FML_DLOG(ERROR) << "napi_create_int64 firstframe fail "; + } + status = fml::napi::InvokeJsMethod(env_, ref_napi_obj_, "onFirstFrame", 1, + callbackParam); + if (status != napi_ok) { + FML_DLOG(ERROR) << "InvokeJsMethod onFirstFrame fail "; + } +} + +void PlatformViewOHOSNapi::FlutterViewOnPreEngineRestart() { + FML_DLOG(INFO) << "FlutterViewOnPreEngineRestart"; + napi_status status = fml::napi::InvokeJsMethod( + env_, ref_napi_obj_, "onPreEngineRestart", 0, nullptr); + if (status != napi_ok) { + FML_DLOG(ERROR) << "InvokeJsMethod onPreEngineRestart fail "; + } +} + +std::vector splitString(const std::string& input, char delimiter) { + std::vector result; + std::stringstream ss(input); + std::string token; + + while (std::getline(ss, token, delimiter)) { + result.push_back(token); + } + + return result; +} + +flutter::locale PlatformViewOHOSNapi::resolveNativeLocale( + std::vector supportedLocales) { + if (supportedLocales.empty()) { + flutter::locale default_locale; + default_locale.language = "zh"; + default_locale.script = "Hans"; + default_locale.region = "CN"; + return default_locale; + } + char delimiter = '-'; + if (PlatformViewOHOSNapi::system_languages.empty()) { + PlatformViewOHOSNapi::system_languages.push_back("zh-Hans"); + } + for (size_t i = 0; i < PlatformViewOHOSNapi::system_languages.size(); i++) { + std::string language = PlatformViewOHOSNapi::system_languages + [i]; // 格式language-script-region,例如en-Latn-US + for (const locale& localeInfo : supportedLocales) { + if (language == localeInfo.language + "-" + localeInfo.script + "-" + + localeInfo.region) { + return localeInfo; + } + std::vector element = splitString(language, delimiter); + if (element[0] + "-" + element[1] == + localeInfo.language + "-" + localeInfo.region) { + return localeInfo; + } + if (element[0] == localeInfo.language) { + return localeInfo; + } + } + } + return supportedLocales[0]; +} + +std::unique_ptr> +PlatformViewOHOSNapi::FlutterViewComputePlatformResolvedLocales( + const std::vector& support_locale_data) { + std::vector supportedLocales; + std::vector result; + const int localeDataLength = 3; + flutter::locale mlocale; + for (size_t i = 0; i < support_locale_data.size(); i += localeDataLength) { + mlocale.language = support_locale_data[i + kLanguageIndex]; + mlocale.region = support_locale_data[i + kRegionIndex]; + mlocale.script = support_locale_data[i + kScriptIndex]; + supportedLocales.push_back(mlocale); + } + mlocale = resolveNativeLocale(supportedLocales); + result.push_back(mlocale.language); + result.push_back(mlocale.region); + result.push_back(mlocale.script); + FML_DLOG(INFO) << "resolveNativeLocale result to flutter language: " + << result[kLanguageIndex] + << " region: " << result[kRegionIndex] + << " script: " << result[kScriptIndex]; + return std::make_unique>(std::move(result)); +} + +void PlatformViewOHOSNapi::FlutterViewOnTouchEvent( + std::shared_ptr touchPacketString, + int size) { + if (touchPacketString == nullptr) { + FML_LOG(ERROR) << "Input parameter error"; + return; + } + napi_value arrayString; + napi_create_array(env_, &arrayString); + + for (int i = 0; i < size; ++i) { + napi_value stringItem; + napi_create_string_utf8(env_, touchPacketString[i].c_str(), -1, + &stringItem); + napi_set_element(env_, arrayString, i, stringItem); + } + + napi_status status = fml::napi::InvokeJsMethod( + env_, ref_napi_obj_, "onTouchEvent", 1, &arrayString); + if (status != napi_ok) { + FML_LOG(ERROR) << "InvokeJsMethod onTouchEvent fail"; + } +} + +/** + * attach flutterNapi实例给到 native + * engine,这个支持rkts到flutter平台的无关引擎之间的通信 attach只需要执行一次 + */ +napi_value PlatformViewOHOSNapi::nativeAttach(napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeAttach"; + + napi_status status; + // 获取传入的参数 + size_t argc = 1; + napi_value argv[1]; + status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + if (status != napi_ok) { + FML_DLOG(ERROR) << "nativeAttach Failed to get napiObjec info"; + } + + std::shared_ptr napi_facade = + std::make_shared(env); + napi_create_reference(env, argv[0], 1, &(napi_facade->ref_napi_obj_)); + + uv_loop_t* platform_loop = nullptr; + status = napi_get_uv_event_loop(env, &platform_loop); + if (status != napi_ok) { + FML_DLOG(ERROR) << "nativeAttach napi_get_uv_event_loop fail"; + } + + if (napi_facade == nullptr) { + FML_DLOG(ERROR) << "napi_facade get nullptr"; + } + + auto shell_holder = std::make_unique( + OhosMain::Get().GetSettings(), napi_facade, platform_loop); + if (shell_holder->IsValid()) { + int64_t shell_holder_value = reinterpret_cast(shell_holder.get()); + FML_DLOG(INFO) << "PlatformViewOHOSNapi shell_holder:" + << shell_holder_value; + napi_value id; + napi_create_int64(env, reinterpret_cast(shell_holder.release()), + &id); + return id; + } else { + FML_DLOG(ERROR) << "shell holder inValid"; + napi_value id; + napi_create_int64(env, 0, &id); + return id; + } +} + +/** + * 加载dart工程构建产物 + */ +napi_value PlatformViewOHOSNapi::nativeRunBundleAndSnapshotFromLibrary( + napi_env env, + napi_callback_info info) { + napi_status ret; + size_t argc = 6; + napi_value args[6] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeRunBundleAndSnapshotFromLibrary napi_get_cb_info error"); + return nullptr; + } + + int64_t shell_holder; + ret = napi_get_value_int64(env, args[0], &shell_holder); + if (ret != napi_ok) { + LOGE("nativeRunBundleAndSnapshotFromLibrary napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeRunBundleAndSnapshotFromLibrary::shell_holder : %{public}ld", + shell_holder); + + std::string bundlePath; + if (fml::napi::kSuccess != fml::napi::GetString(env, args[1], bundlePath)) { + LOGE(" napi_get_value_string_utf8 error"); + return nullptr; + } + LOGD("nativeRunBundleAndSnapshotFromLibrary: bundlePath: %{public}s", + bundlePath.c_str()); + + std::string entrypointFunctionName; + if (fml::napi::kSuccess != + fml::napi::GetString(env, args[2], entrypointFunctionName)) { + LOGE(" napi_get_value_string_utf8 error"); + return nullptr; + } + LOGD("entrypointFunctionName: %{public}s", entrypointFunctionName.c_str()); + + std::string pathToEntrypointFunction; + if (fml::napi::kSuccess != + fml::napi::GetString(env, args[3], pathToEntrypointFunction)) { + LOGE(" napi_get_value_string_utf8 error"); + return nullptr; + } + LOGD(" pathToEntrypointFunction: %{public}s", + pathToEntrypointFunction.c_str()); + + NativeResourceManager* ResourceManager = + OH_ResourceManager_InitNativeResourceManager(env, args[4]); + + std::vector entrypointArgs; + if (fml::napi::kSuccess != + fml::napi::GetArrayString(env, args[5], entrypointArgs)) { + LOGE("nativeRunBundleAndSnapshotFromLibrary GetArrayString error"); + return nullptr; + } + + auto ohos_asset_provider = std::make_unique( + static_cast(ResourceManager)); + OHOS_SHELL_HOLDER->Launch(std::move(ohos_asset_provider), + entrypointFunctionName, pathToEntrypointFunction, + entrypointArgs); + + env_ = env; + return nullptr; +} + +/** + * 设置ResourceManager和assetBundlePath到engine + */ +napi_value PlatformViewOHOSNapi::nativeUpdateOhosAssetManager( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeUpdateOhosAssetManager"); + + return nullptr; +} + +/** + * 从engine获取当前绘制pixelMap + */ +napi_value PlatformViewOHOSNapi::nativeGetPixelMap(napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeGetPixelMap"); + + return nullptr; +} + +/** + * 从当前的flutterNapi复制一个新的实例 + */ +napi_value PlatformViewOHOSNapi::nativeSpawn(napi_env env, + napi_callback_info info) { + napi_status ret; + size_t argc = 6; + napi_value args[6] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeSpawn napi_get_cb_info error"); + return nullptr; + } + + int64_t shell_holder; + ret = napi_get_value_int64(env, args[0], &shell_holder); + if (ret != napi_ok) { + LOGE("nativeSpawn napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSpawn::shell_holder : %{public}ld", shell_holder); + + std::string entrypoint; + if (fml::napi::kSuccess != fml::napi::GetString(env, args[1], entrypoint)) { + LOGE(" napi_get_value_string_utf8 error"); + return nullptr; + } + LOGD("entrypoint: %{public}s", entrypoint.c_str()); + + std::string libraryUrl; + if (fml::napi::kSuccess != fml::napi::GetString(env, args[2], libraryUrl)) { + LOGE(" napi_get_value_string_utf8 error"); + return nullptr; + } + LOGD(" libraryUrl: %{public}s", libraryUrl.c_str()); + + std::string initial_route; + if (fml::napi::kSuccess != + fml::napi::GetString(env, args[3], initial_route)) { + LOGE(" napi_get_value_string_utf8 error"); + return nullptr; + } + LOGD(" initialRoute: %{public}s", initial_route.c_str()); + + std::vector entrypoint_args; + if (fml::napi::kSuccess != + fml::napi::GetArrayString(env, args[4], entrypoint_args)) { + LOGE("nativeRunBundleAndSnapshotFromLibrary GetArrayString error"); + return nullptr; + } + + std::shared_ptr napi_facade = + std::make_shared(env); + napi_create_reference(env, args[5], 1, &(napi_facade->ref_napi_obj_)); + + auto spawned_shell_holder = OHOS_SHELL_HOLDER->Spawn( + napi_facade, entrypoint, libraryUrl, initial_route, entrypoint_args); + + if (spawned_shell_holder == nullptr || !spawned_shell_holder->IsValid()) { + FML_LOG(ERROR) << "Could not spawn Shell"; + return nullptr; + } + + napi_value shell_holder_id; + napi_create_int64(env, + reinterpret_cast(spawned_shell_holder.release()), + &shell_holder_id); + return shell_holder_id; +} + +static void LoadLoadingUnitFailure(intptr_t loading_unit_id, + const std::string& message, + bool transient) { + // TODO(garyq): Implement + LOGD("LoadLoadingUnitFailure: message %s transient %d", message.c_str(), + transient); +} + +/** + * load一个合法的.so文件到dart vm + */ +napi_value PlatformViewOHOSNapi::nativeLoadDartDeferredLibrary( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeLoadDartDeferredLibrary"); + + napi_status ret; + size_t argc = 3; + napi_value args[3] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeLoadDartDeferredLibrary napi_get_cb_info error"); + return nullptr; + } + + int64_t shell_holder; + ret = napi_get_value_int64(env, args[0], &shell_holder); + if (ret != napi_ok) { + LOGE("nativeLoadDartDeferredLibrary napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeLoadDartDeferredLibrary::shell_holder : %{public}ld", + shell_holder); + + int64_t loadingUnitId; + ret = napi_get_value_int64(env, args[1], &loadingUnitId); + if (ret != napi_ok) { + LOGE("nativeLoadDartDeferredLibrary napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeLoadDartDeferredLibrary::loadingUnitId : %{public}ld", + loadingUnitId); + + std::vector search_paths; + if (fml::napi::kSuccess != + fml::napi::GetArrayString(env, args[2], search_paths)) { + LOGE("nativeLoadDartDeferredLibrary GetArrayString error"); + return nullptr; + } + + LOGD("nativeLoadDartDeferredLibrary::search_paths"); + for (const std::string& path : search_paths) { + LOGD("%{public}s", path.c_str()); + } + + intptr_t loading_unit_id = static_cast(loadingUnitId); + // Use dlopen here to directly check if handle is nullptr before creating a + // NativeLibrary. + void* handle = nullptr; + while (handle == nullptr && !search_paths.empty()) { + std::string path = search_paths.back(); + handle = ::dlopen(path.c_str(), RTLD_NOW); + search_paths.pop_back(); + } + if (handle == nullptr) { + LoadLoadingUnitFailure(loading_unit_id, + "No lib .so found for provided search paths.", true); + return nullptr; + } + fml::RefPtr native_lib = + fml::NativeLibrary::CreateWithHandle(handle, false); + + // Resolve symbols. + std::unique_ptr data_mapping = + std::make_unique( + native_lib, DartSnapshot::kIsolateDataSymbol); + std::unique_ptr instructions_mapping = + std::make_unique( + native_lib, DartSnapshot::kIsolateInstructionsSymbol); + + OHOS_SHELL_HOLDER->GetPlatformView()->LoadDartDeferredLibrary( + loading_unit_id, std::move(data_mapping), + std::move(instructions_mapping)); + + return nullptr; +} + +/** + * 把物理屏幕参数通知到native + */ +napi_value PlatformViewOHOSNapi::nativeSetViewportMetrics( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeSetViewportMetrics"); + + napi_status ret; + size_t argc = 20; + napi_value args[20] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_cb_info error"); + return nullptr; + } + + int64_t shell_holder; + ret = napi_get_value_int64(env, args[0], &shell_holder); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::shell_holder : %{public}ld", shell_holder); + + double devicePixelRatio; + ret = napi_get_value_double(env, args[1], &devicePixelRatio); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::devicePixelRatio : %{public}lf", + devicePixelRatio); + + int64_t physicalWidth; + ret = napi_get_value_int64(env, args[2], &physicalWidth); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::physicalWidth : %{public}ld", physicalWidth); + + int64_t physicalHeight; + ret = napi_get_value_int64(env, args[3], &physicalHeight); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::physicalHeight : %{public}ld", + physicalHeight); + + int64_t physicalPaddingTop; + ret = napi_get_value_int64(env, args[4], &physicalPaddingTop); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::physicalPaddingTop : %{public}ld", + physicalPaddingTop); + + int64_t physicalPaddingRight; + ret = napi_get_value_int64(env, args[5], &physicalPaddingRight); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::physicalPaddingRight : %{public}ld", + physicalPaddingRight); + + int64_t physicalPaddingBottom; + ret = napi_get_value_int64(env, args[6], &physicalPaddingBottom); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::physicalPaddingBottom : %{public}ld", + physicalPaddingBottom); + + int64_t physicalPaddingLeft; + ret = napi_get_value_int64(env, args[7], &physicalPaddingLeft); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::physicalPaddingLeft : %{public}ld", + physicalPaddingLeft); + + int64_t physicalViewInsetTop; + ret = napi_get_value_int64(env, args[8], &physicalViewInsetTop); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::physicalViewInsetTop : %{public}ld", + physicalViewInsetTop); + + int64_t physicalViewInsetRight; + ret = napi_get_value_int64(env, args[9], &physicalViewInsetRight); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::physicalViewInsetRight : %{public}ld", + physicalViewInsetRight); + + int64_t physicalViewInsetBottom; + ret = napi_get_value_int64(env, args[10], &physicalViewInsetBottom); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::physicalViewInsetBottom : %{public}ld", + physicalViewInsetBottom); + + int64_t physicalViewInsetLeft; + ret = napi_get_value_int64(env, args[11], &physicalViewInsetLeft); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::physicalViewInsetLeft : %{public}ld", + physicalViewInsetLeft); + int64_t systemGestureInsetTop; + ret = napi_get_value_int64(env, args[12], &systemGestureInsetTop); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::systemGestureInsetTop : %{public}ld", + systemGestureInsetTop); + int64_t systemGestureInsetRight; + ret = napi_get_value_int64(env, args[13], &systemGestureInsetRight); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::systemGestureInsetRight : %{public}ld", + systemGestureInsetRight); + + int64_t systemGestureInsetBottom; + ret = napi_get_value_int64(env, args[14], &systemGestureInsetBottom); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::systemGestureInsetBottom : %{public}ld", + systemGestureInsetBottom); + int64_t systemGestureInsetLeft; + ret = napi_get_value_int64(env, args[15], &systemGestureInsetLeft); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::systemGestureInsetLeft : %{public}ld", + systemGestureInsetLeft); + + double physicalTouchSlop; + ret = napi_get_value_double(env, args[16], &physicalTouchSlop); + if (ret != napi_ok) { + LOGE("nativeSetViewportMetrics napi_get_value_double error"); + return nullptr; + } + LOGD("nativeSetViewportMetrics::physicalTouchSlop : %{public}lf", + physicalTouchSlop); + + std::vector displayFeaturesBounds; + napi_value array = args[17]; + uint32_t length; + napi_get_array_length(env, array, &length); + displayFeaturesBounds.resize(length); + for (uint32_t i = 0; i < length; ++i) { + napi_value element; + napi_get_element(env, array, i, &element); + napi_get_value_double(env, element, &(displayFeaturesBounds[i])); + } + + LOGD("nativeSetViewportMetrics::displayFeaturesBounds"); + for (const uint64_t& bounds : displayFeaturesBounds) { + LOGD(" %{public}ld", bounds); + } + + std::vector displayFeaturesType; + array = args[18]; + napi_get_array_length(env, array, &length); + displayFeaturesType.resize(length); + for (uint32_t i = 0; i < length; ++i) { + napi_value element; + napi_get_element(env, array, i, &element); + napi_get_value_int64(env, element, &(displayFeaturesType[i])); + } + + LOGD("nativeSetViewportMetrics::displayFeaturesType"); + for (const uint64_t& featuresType : displayFeaturesType) { + LOGD(" %{public}ld", featuresType); + } + + std::vector displayFeaturesState; + array = args[19]; + napi_get_array_length(env, array, &length); + displayFeaturesState.resize(length); + for (uint32_t i = 0; i < length; ++i) { + napi_value element; + napi_get_element(env, array, i, &element); + napi_get_value_int64(env, element, &(displayFeaturesState[i])); + } + + LOGD("nativeSetViewportMetrics::displayFeaturesState"); + for (const uint64_t& featurestate : displayFeaturesState) { + LOGD(" %{public}ld", featurestate); + } + + flutter::ViewportMetrics metrics{ + static_cast(devicePixelRatio), + static_cast(physicalWidth), + static_cast(physicalHeight), + static_cast(physicalPaddingTop), + static_cast(physicalPaddingRight), + static_cast(physicalPaddingBottom), + static_cast(physicalPaddingLeft), + static_cast(physicalViewInsetTop), + static_cast(physicalViewInsetRight), + static_cast(physicalViewInsetBottom), + static_cast(physicalViewInsetLeft), + static_cast(systemGestureInsetTop), + static_cast(systemGestureInsetRight), + static_cast(systemGestureInsetBottom), + static_cast(systemGestureInsetLeft), + static_cast(physicalTouchSlop), + displayFeaturesBounds, + std::vector(displayFeaturesType.begin(), displayFeaturesType.end()), + std::vector(displayFeaturesState.begin(), + displayFeaturesState.end()), + 0, // Display ID + }; + + OHOS_SHELL_HOLDER->GetPlatformView()->SetViewportMetrics( + kFlutterImplicitViewId, metrics); + + return nullptr; +} + +/** + * 清除某个messageData + */ +napi_value PlatformViewOHOSNapi::nativeCleanupMessageData( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeCleanupMessageData"); + + napi_status ret; + size_t argc = 1; + napi_value args[1] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeCleanupMessageData napi_get_cb_info error"); + return nullptr; + } + + int64_t messageData; + ret = napi_get_value_int64(env, args[0], &messageData); + if (ret != napi_ok) { + LOGE("nativeCleanupMessageData napi_get_value_int64 error"); + return nullptr; + } + + LOGD("nativeCleanupMessageData messageData: %{public}ld", messageData); + free(reinterpret_cast(messageData)); + return nullptr; +} + +/** + * 设置刷新率 + */ +napi_value PlatformViewOHOSNapi::nativeUpdateRefreshRate( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeUpdateRefreshRate"); + + int64_t refreshRate; + napi_status ret; + size_t argc = 1; + napi_value args[1] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeUpdateRefreshRate napi_get_cb_info error"); + return nullptr; + } + + ret = napi_get_value_int64(env, args[0], &refreshRate); + if (ret != napi_ok) { + LOGE("nativeUpdateRefreshRate napi_get_value_int64 error"); + return nullptr; + } + + LOGD("PlatformViewOHOSNapi::nativeUpdateRefreshRate: %{public}ld", + refreshRate); + FML_DCHECK(refreshRate > 0); + display_refresh_rate = refreshRate; + return nullptr; +} + +/** + * 设置屏幕尺寸 + */ +napi_value PlatformViewOHOSNapi::nativeUpdateSize(napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeUpdateSize"); + + int64_t width; + int64_t height; + napi_status ret; + size_t argc = 2; + napi_value args[2] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeUpdateSize napi_get_cb_info error"); + return nullptr; + } + + ret = napi_get_value_int64(env, args[0], &width); + if (ret != napi_ok) { + LOGE("nativeUpdateSize napi_get_value_int64 error"); + return nullptr; + } + + ret = napi_get_value_int64(env, args[1], &height); + if (ret != napi_ok) { + LOGE("nativeUpdateSize napi_get_value_int64 error"); + return nullptr; + } + + LOGD("PlatformViewOHOSNapi::nativeUpdateSize: %{public}ld %{public}ld", width, + height); + FML_DCHECK(width > 0); + FML_DCHECK(height > 0); + display_width = width; + display_height = height; + return nullptr; +} + +/** + * 设置屏幕像素密度(也就是缩放系数) + */ +napi_value PlatformViewOHOSNapi::nativeUpdateDensity(napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeUpdateDensity"); + + double densityPixels; + napi_status ret; + size_t argc = 1; + napi_value args[1] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeUpdateDensity napi_get_cb_info error"); + return nullptr; + } + + ret = napi_get_value_double(env, args[0], &densityPixels); + if (ret != napi_ok) { + LOGE("nativeUpdateDensity napi_get_value_double error"); + return nullptr; + } + + LOGD("PlatformViewOHOSNapi::nativeUpdateDensity: %{public}lf", densityPixels); + FML_DCHECK(densityPixels > 0); + display_density_pixels = densityPixels; + return nullptr; +} + +/** + * 初始化SkFontMgr::RefDefault(),skia引擎文字管理初始化 + */ +napi_value PlatformViewOHOSNapi::nativePrefetchDefaultFontManager( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativePrefetchDefaultFontManager"); + + OHOSShellHolder::InitializeSystemFont(); + return nullptr; +} + +/** + * hot reload font + */ +napi_value PlatformViewOHOSNapi::nativeCheckAndReloadFont( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeCheckAndReloadFont"); + + napi_status ret; + size_t argc = 1; + napi_value args[1] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeCheckAndReloadFont napi_get_cb_info error"); + return nullptr; + } + int64_t shell_holder; + ret = napi_get_value_int64(env, args[0], &shell_holder); + if (ret != napi_ok) { + LOGE("nativeCheckAndReloadFont napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeCheckAndReloadFont shell_holder: %{public}ld", shell_holder); + OHOS_SHELL_HOLDER->ReloadSystemFonts(); + return nullptr; +} + +/** + * 返回是否支持软件绘制 + */ +napi_value PlatformViewOHOSNapi::nativeGetIsSoftwareRenderingEnabled( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeGetIsSoftwareRenderingEnabled"); + napi_value result = nullptr; + // TODO: 需要 FlutterMain 初始化 + napi_status ret = napi_get_boolean( + env, OhosMain::Get().GetSettings().enable_software_rendering, &result); + if (ret != napi_ok) { + LOGE("nativeGetIsSoftwareRenderingEnabled napi_get_boolean error"); + return nullptr; + } + + return result; +} + +/** + * Detaches flutterNapi和engine之间的关联 + */ +napi_value PlatformViewOHOSNapi::nativeDestroy(napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeDestroy"); + + napi_status ret; + size_t argc = 1; + napi_value args[1] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeDestroy napi_get_cb_info error"); + return nullptr; + } + + int64_t shell_holder; + ret = napi_get_value_int64(env, args[0], &shell_holder); + if (ret != napi_ok) { + LOGE("nativeDestroy napi_get_value_int64 error"); + return nullptr; + } + + LOGD("nativeDestroy shell_holder: %{public}ld", shell_holder); + + delete OHOS_SHELL_HOLDER; + return nullptr; +} + +/** + * 设置能力参数 + */ +napi_value PlatformViewOHOSNapi::nativeSetAccessibilityFeatures( + napi_env env, + napi_callback_info info) { + LOGD("nativeSetAccessibilityFeatures"); + + napi_status ret; + size_t argc = 2; + napi_value args[2] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeSetAccessibilityFeatures napi_get_cb_info error"); + return nullptr; + } + + int64_t shell_holder; + ret = napi_get_value_int64(env, args[0], &shell_holder); + if (ret != napi_ok) { + LOGE("nativeDestroy napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeSetAccessibilityFeatures shell_holder: %{public}ld", + shell_holder); + int64_t flags; + ret = napi_get_value_int64(env, args[1], &flags); + if (ret != napi_ok) { + LOGE("nativeSetAccessibilityFeatures napi_get_value_int64 error"); + return nullptr; + } + LOGD( + "PlatformViewOHOSNapi::nativeSetAccessibilityFeatures flags: %{public}ld", + flags); + OHOS_SHELL_HOLDER->GetPlatformView()->SetAccessibilityFeatures(flags); + return nullptr; +} + +/** + * 加载动态库,或者dart库失败时的通知 + */ +napi_value PlatformViewOHOSNapi::nativeDeferredComponentInstallFailure( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeDeferredComponentInstallFailure"); + + napi_status ret; + size_t argc = 3; + napi_value args[3] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeDeferredComponentInstallFailure napi_get_cb_info error"); + return nullptr; + } + + int64_t loadingUnitId; + ret = napi_get_value_int64(env, args[0], &loadingUnitId); + if (ret != napi_ok) { + LOGE("nativeDeferredComponentInstallFailure napi_get_value_int64 error"); + return nullptr; + } + LOGD( + "PlatformViewOHOSNapi::nativeSetAccessibilityFeatures loadingUnitId: " + "%{public}ld", + loadingUnitId); + std::string error; + if (fml::napi::kSuccess != fml::napi::GetString(env, args[1], error)) { + LOGE( + "nativeDeferredComponentInstallFailure napi_get_value_string_utf8 " + "error"); + return nullptr; + } + LOGD("nativeSetAccessibilityFeatures loadingUnitId: %s", error.c_str()); + + bool isTransient; + ret = napi_get_value_bool(env, args[2], &isTransient); + if (ret != napi_ok) { + LOGE("nativeDeferredComponentInstallFailure napi_get_value_bool error"); + return nullptr; + } + LOGD("nativeSetAccessibilityFeatures loadingUnitId: %{public}d", isTransient); + + LoadLoadingUnitFailure(static_cast(loadingUnitId), + std::string(error), static_cast(isTransient)); + + return nullptr; +} + +/** + * 应用低内存警告 + */ +napi_value PlatformViewOHOSNapi::nativeNotifyLowMemoryWarning( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeNotifyLowMemoryWarning"); + + napi_status ret; + size_t argc = 1; + napi_value args[1] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeNotifyLowMemoryWarning napi_get_cb_info error"); + return nullptr; + } + + int64_t shell_holder; + ret = napi_get_value_int64(env, args[0], &shell_holder); + if (ret != napi_ok) { + LOGE("nativeNotifyLowMemoryWarning napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeNotifyLowMemoryWarning shell_holder: %{public}ld", shell_holder); + OHOS_SHELL_HOLDER->NotifyLowMemoryWarning(); + + return nullptr; +} + +// 下面的方法,从键盘输入中判断当前字符是否是emoji + +/** + * + */ +napi_value PlatformViewOHOSNapi::nativeFlutterTextUtilsIsEmoji( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeFlutterTextUtilsIsEmoji"); + napi_status ret; + size_t argc = 1; + napi_value args[1] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeFlutterTextUtilsIsEmoji napi_get_cb_info error"); + return nullptr; + } + + int64_t codePoint; + ret = napi_get_value_int64(env, args[0], &codePoint); + if (ret != napi_ok) { + LOGE("nativeFlutterTextUtilsIsEmoji napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeFlutterTextUtilsIsEmoji codePoint: %{public}ld ", codePoint); + + bool value = u_hasBinaryProperty(codePoint, UProperty::UCHAR_EMOJI); + napi_value result = nullptr; + ret = napi_get_boolean(env, value, &result); + if (ret != napi_ok) { + LOGE("nativeFlutterTextUtilsIsEmoji napi_get_boolean error"); + return nullptr; + } + + return result; +} + +/** + * + */ +napi_value PlatformViewOHOSNapi::nativeFlutterTextUtilsIsEmojiModifier( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeFlutterTextUtilsIsEmojiModifier"); + + napi_status ret; + size_t argc = 1; + napi_value args[1] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeFlutterTextUtilsIsEmojiModifier napi_get_cb_info error"); + return nullptr; + } + + int64_t codePoint; + ret = napi_get_value_int64(env, args[0], &codePoint); + if (ret != napi_ok) { + LOGE("nativeFlutterTextUtilsIsEmojiModifier napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeFlutterTextUtilsIsEmojiModifier codePoint: %{public}ld ", + codePoint); + + bool value = u_hasBinaryProperty(codePoint, UProperty::UCHAR_EMOJI_MODIFIER); + napi_value result = nullptr; + ret = napi_get_boolean(env, value, &result); + if (ret != napi_ok) { + LOGE("nativeFlutterTextUtilsIsEmojiModifier napi_get_boolean error"); + return nullptr; + } + + return result; +} + +/** + * + */ +napi_value PlatformViewOHOSNapi::nativeFlutterTextUtilsIsEmojiModifierBase( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeFlutterTextUtilsIsEmojiModifierBase"); + + napi_status ret; + size_t argc = 1; + napi_value args[1] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeFlutterTextUtilsIsEmojiModifierBase napi_get_cb_info error"); + return nullptr; + } + + int64_t codePoint; + ret = napi_get_value_int64(env, args[0], &codePoint); + if (ret != napi_ok) { + LOGE( + "nativeFlutterTextUtilsIsEmojiModifierBase napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeFlutterTextUtilsIsEmojiModifierBase codePoint: %{public}ld ", + codePoint); + + bool value = + u_hasBinaryProperty(codePoint, UProperty::UCHAR_EMOJI_MODIFIER_BASE); + napi_value result = nullptr; + ret = napi_get_boolean(env, value, &result); + if (ret != napi_ok) { + LOGE("nativeFlutterTextUtilsIsEmojiModifierBase napi_get_boolean error"); + return nullptr; + } + + return result; +} + +/** + * + */ +napi_value PlatformViewOHOSNapi::nativeFlutterTextUtilsIsVariationSelector( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeFlutterTextUtilsIsVariationSelector"); + + napi_status ret; + size_t argc = 1; + napi_value args[1] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeFlutterTextUtilsIsVariationSelector napi_get_cb_info error"); + return nullptr; + } + + int64_t codePoint; + ret = napi_get_value_int64(env, args[0], &codePoint); + if (ret != napi_ok) { + LOGE( + "nativeFlutterTextUtilsIsVariationSelector napi_get_value_int64 error"); + return nullptr; + } + LOGD("nativeFlutterTextUtilsIsVariationSelector codePoint: %{public}ld ", + codePoint); + + bool value = + u_hasBinaryProperty(codePoint, UProperty::UCHAR_VARIATION_SELECTOR); + napi_value result = nullptr; + ret = napi_get_boolean(env, value, &result); + if (ret != napi_ok) { + LOGE("nativeFlutterTextUtilsIsVariationSelector napi_get_boolean error"); + return nullptr; + } + + return result; +} + +/** + * + */ +napi_value PlatformViewOHOSNapi::nativeFlutterTextUtilsIsRegionalIndicator( + napi_env env, + napi_callback_info info) { + LOGD("PlatformViewOHOSNapi::nativeFlutterTextUtilsIsRegionalIndicator"); + + napi_status ret; + size_t argc = 1; + napi_value args[1] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeFlutterTextUtilsIsRegionalIndicator napi_get_cb_info error"); + return nullptr; + } + + int64_t codePoint; + ret = napi_get_value_int64(env, args[0], &codePoint); + if (ret != napi_ok) { + LOGE("nativeFlutterTextUtilsIsRegionalIndicator napi_get_value_int64 fail"); + return nullptr; + } + LOGD("nativeFlutterTextUtilsIsRegionalIndicator codePoint: %{public}ld ", + codePoint); + + bool value = + u_hasBinaryProperty(codePoint, UProperty::UCHAR_REGIONAL_INDICATOR); + napi_value result = nullptr; + ret = napi_get_boolean(env, value, &result); + if (ret != napi_ok) { + LOGE("nativeFlutterTextUtilsIsRegionalIndicator napi_get_boolean error"); + return nullptr; + } + + return result; +} + +/** + * @brief ArkTS下发系统语言设置列表 + * @note + * @param nativeShellHolderId: number + * @param systemLanguages: Array + * @return void + */ +napi_value PlatformViewOHOSNapi::nativeGetSystemLanguages( + napi_env env, + napi_callback_info info) { + napi_status ret; + size_t argc = 2; + napi_value args[2] = {nullptr}; + int64_t shell_holder; + std::vector local_languages; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeGetSystemLanguages napi_get_cb_info error:" + << ret; + return nullptr; + } + ret = napi_get_value_int64(env, args[0], &shell_holder); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeGetSystemLanguages napi_get_value_int64 error"; + return nullptr; + } + if (fml::napi::kSuccess != + fml::napi::GetArrayString(env, args[1], local_languages)) { + FML_DLOG(ERROR) << "nativeGetSystemLanguages GetArrayString error"; + return nullptr; + } + system_languages = local_languages; + return nullptr; +} + +napi_value PlatformViewOHOSNapi::nativeRegisterTexture( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeRegisterTexture"; + size_t argc = 2; + napi_value args[2] = {nullptr}; + int64_t shell_holder; + int64_t textureId; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_int64(env, args[1], &textureId)); + int64_t surfaceId = + OHOS_SHELL_HOLDER->GetPlatformView()->RegisterExternalTexture(textureId); + napi_value res; + napi_create_int64(env, surfaceId, &res); + return res; +} + +napi_value PlatformViewOHOSNapi::nativeUnregisterTexture( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeUnregisterTexture"; + size_t argc = 2; + napi_value args[2] = {nullptr}; + int64_t shell_holder; + int64_t textureId; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_int64(env, args[1], &textureId)); + OHOS_SHELL_HOLDER->GetPlatformView()->UnRegisterExternalTexture(textureId); + return nullptr; +} + +napi_value PlatformViewOHOSNapi::nativeGetTextureWindowId( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeGetTextureWindowId"; + size_t argc = 2; + napi_value args[2] = {nullptr}; + int64_t shell_holder; + int64_t textureId; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_int64(env, args[1], &textureId)); + uint64_t windowId = + OHOS_SHELL_HOLDER->GetPlatformView()->GetExternalTextureWindowId( + textureId); + napi_value res; + napi_create_int64(env, windowId, &res); + return res; +} + +napi_value PlatformViewOHOSNapi::nativeSetTextureBufferSize( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeSetTextureBufferSize"; + size_t argc = 4; + napi_value args[4] = {nullptr}; + int64_t shell_holder; + int64_t textureId; + int32_t width; + int32_t height; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_int64(env, args[1], &textureId)); + NAPI_CALL(env, napi_get_value_int32(env, args[2], &width)); + NAPI_CALL(env, napi_get_value_int32(env, args[3], &height)); + OHOS_SHELL_HOLDER->GetPlatformView()->SetTextureBufferSize(textureId, width, + height); + return nullptr; +} + +napi_value PlatformViewOHOSNapi::nativeNotifyTextureResizing( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeNotifyTextureResizing"; + size_t argc = 4; + napi_value args[4] = {nullptr}; + int64_t shell_holder; + int64_t textureId; + int32_t width; + int32_t height; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_int64(env, args[1], &textureId)); + NAPI_CALL(env, napi_get_value_int32(env, args[2], &width)); + NAPI_CALL(env, napi_get_value_int32(env, args[3], &height)); + OHOS_SHELL_HOLDER->GetPlatformView()->NotifyTextureResizing(textureId, width, + height); + return nullptr; +} + +napi_value PlatformViewOHOSNapi::nativeSetExternalNativeImage( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeSetExternalNativeImage"; + size_t argc = 3; + napi_value args[3] = {nullptr}; + int64_t shell_holder; + int64_t textureId; + int64_t native_image_ptr; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_int64(env, args[1], &textureId)); + NAPI_CALL(env, napi_get_value_int64(env, args[2], &native_image_ptr)); + + OH_NativeImage* native_image = + (reinterpret_cast(native_image_ptr)); + + bool ret = OHOS_SHELL_HOLDER->GetPlatformView()->SetExternalNativeImage( + textureId, native_image); + napi_value res; + napi_create_int64(env, (int64_t)ret, &res); + return res; +} + +napi_value PlatformViewOHOSNapi::nativeResetExternalTexture( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeResetExternalTexture"; + size_t argc = 3; + napi_value args[3] = {nullptr}; + int64_t shell_holder; + int64_t textureId; + bool need_surfaceId; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_int64(env, args[1], &textureId)); + NAPI_CALL(env, napi_get_value_bool(env, args[2], &need_surfaceId)); + + uint64_t surface_id = + OHOS_SHELL_HOLDER->GetPlatformView()->ResetExternalTexture( + textureId, need_surfaceId); + napi_value res; + napi_create_int64(env, surface_id, &res); + return res; +} + +napi_value PlatformViewOHOSNapi::nativeMarkTextureFrameAvailable( + napi_env env, + napi_callback_info info) { + size_t argc = 2; + napi_value args[2] = {nullptr}; + int64_t shell_holder; + int64_t textureId; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_int64(env, args[1], &textureId)); + OHOS_SHELL_HOLDER->GetPlatformView()->MarkTextureFrameAvailable(textureId); + return nullptr; +} + +static OH_NativeBuffer* GetNativeBufferFromPixelMap(napi_env env, + napi_value pixel_map) { + OH_PixelmapNative* pixelMap_native = nullptr; + OH_NativeBuffer* native_buffer = nullptr; + OH_PixelmapNative_ConvertPixelmapNativeFromNapi(env, pixel_map, + &pixelMap_native); + if (pixelMap_native) { + // Once a NativeBuffer is obtained, Reference is automatically called, so it + // needs to be Unreferenced before it can be released. + OH_PixelmapNative_GetNativeBuffer(pixelMap_native, &native_buffer); + OH_PixelmapNative_Release(pixelMap_native); + } + return native_buffer; +} + +napi_value PlatformViewOHOSNapi::nativeRegisterPixelMap( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeRegisterPixelMap"; + size_t argc = 3; + napi_value args[3] = {nullptr}; + int64_t shell_holder; + int64_t textureId; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_int64(env, args[1], &textureId)); + NativePixelMap* nativePixelMap = OH_PixelMap_InitNativePixelMap(env, args[2]); + OH_NativeBuffer* native_buffer = GetNativeBufferFromPixelMap(env, args[2]); + + OHOS_SHELL_HOLDER->GetPlatformView()->RegisterExternalTextureByPixelMap( + textureId, nativePixelMap, native_buffer); + return nullptr; +} + +napi_value PlatformViewOHOSNapi::nativeSetTextureBackGroundPixelMap( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeSetTextureBackGroundPixelMap"; + size_t argc = 3; + napi_value args[3] = {nullptr}; + int64_t shell_holder; + int64_t textureId; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_int64(env, args[1], &textureId)); + NativePixelMap* nativePixelMap = OH_PixelMap_InitNativePixelMap(env, args[2]); + OH_NativeBuffer* native_buffer = GetNativeBufferFromPixelMap(env, args[2]); + + OHOS_SHELL_HOLDER->GetPlatformView()->SetExternalTextureBackGroundPixelMap( + textureId, nativePixelMap, native_buffer); + return nullptr; +} + +napi_value PlatformViewOHOSNapi::nativeEnableFrameCache( + napi_env env, + napi_callback_info info) { + size_t argc = 2; + napi_value args[2] = {nullptr}; + int64_t shell_holder; + bool enable; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_bool(env, args[1], &enable)); + + OHOS_SHELL_HOLDER->GetPlatformView()->EnableFrameCache(enable); + return nullptr; +} + +void PlatformViewOHOSNapi::SurfaceCreated(int64_t shell_holder, + void* window, + int width, + int height) { + auto native_window = fml::MakeRefCounted( + static_cast(window)); + OHOS_SHELL_HOLDER->GetPlatformView()->UpdateDisplaySize(width, height); + OHOS_SHELL_HOLDER->GetPlatformView()->NotifyCreate(std::move(native_window)); +} + +void PlatformViewOHOSNapi::SurfacePreload(int64_t shell_holder, + int width, + int height) { + OHOS_SHELL_HOLDER->GetPlatformView()->UpdateDisplaySize(width, height); + OHOS_SHELL_HOLDER->GetPlatformView()->Preload(width, height); +} + +void PlatformViewOHOSNapi::SurfaceChanged(int64_t shell_holder, + void* window, + int width, + int height) { + FML_LOG(INFO) << "impeller" << "SurfaceChanged:"; + auto native_window = fml::MakeRefCounted( + static_cast(window)); + OHOS_SHELL_HOLDER->GetPlatformView()->UpdateDisplaySize(width, height); + OHOS_SHELL_HOLDER->GetPlatformView()->NotifySurfaceWindowChanged( + native_window); +} + +void PlatformViewOHOSNapi::SurfaceDestroyed(int64_t shell_holder) { + OHOS_SHELL_HOLDER->GetPlatformView()->NotifyDestroyed(); +} + +void PlatformViewOHOSNapi::SetPlatformTaskRunner( + fml::RefPtr platform_task_runner) { + platform_task_runner_ = platform_task_runner; +} + +/** + * @brief xcomponent与flutter引擎绑定 + * @note + * @param nativeShellHolderId: number + * @param xcomponentId: number + * @return void + */ +napi_value PlatformViewOHOSNapi::nativeXComponentAttachFlutterEngine( + napi_env env, + napi_callback_info info) { + napi_status ret; + size_t argc = 2; + napi_value args[2] = {nullptr}; + std::string xcomponent_id; + int64_t shell_holder; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + FML_DLOG(ERROR) + << "nativeXComponentAttachFlutterEngine napi_get_cb_info error:" << ret; + return nullptr; + } + + if (fml::napi::GetString(env, args[0], xcomponent_id) != 0) { + FML_DLOG(ERROR) + << "nativeXComponentAttachFlutterEngine xcomponent_id GetString error"; + return nullptr; + } + + ret = napi_get_value_int64(env, args[1], &shell_holder); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeXComponentAttachFlutterEngine shell_holder " + "napi_get_value_int64 error"; + return nullptr; + } + std::string shell_holder_str = std::to_string(shell_holder); + + LOGD( + "nativeXComponentAttachFlutterEngine xcomponent_id: %{public}s, " + "shell_holder: %{public}ld ", + xcomponent_id.c_str(), shell_holder); + + XComponentAdapter::GetInstance()->AttachFlutterEngine(xcomponent_id, + shell_holder_str); + return nullptr; +} + +/** + * @brief 提前绘制xcomponent的内容 + * @note + * @param nativeShellHolderId: number + * @param xcomponentId: number + * @return void + */ +napi_value PlatformViewOHOSNapi::nativeXComponentPreDraw( + napi_env env, + napi_callback_info info) { + napi_status ret; + size_t argc = 4; + napi_value args[4] = {nullptr}; + std::string xcomponent_id; + int64_t shell_holder; + int width = 0; + int height = 0; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeXComponentPreDraw napi_get_cb_info error:" << ret; + return nullptr; + } + + if (fml::napi::GetString(env, args[0], xcomponent_id) != 0) { + FML_DLOG(ERROR) << "nativeXComponentPreDraw xcomponent_id GetString error"; + return nullptr; + } + + ret = napi_get_value_int64(env, args[1], &shell_holder); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeXComponentPreDraw shell_holder " + "napi_get_value_int64 error"; + return nullptr; + } + std::string shell_holder_str = std::to_string(shell_holder); + + ret = napi_get_value_int32(env, args[2], &width); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeXComponentPreDraw width " + "napi_get_value_int32 error"; + return nullptr; + } + + ret = napi_get_value_int32(env, args[3], &height); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeXComponentPreDraw height " + "napi_get_value_int32 error"; + return nullptr; + } + + LOGD( + "nativeXComponentPreDraw xcomponent_id: %{public}s, " + "shell_holder: %{public}ld ", + xcomponent_id.c_str(), shell_holder); + + XComponentAdapter::GetInstance()->PreDraw(xcomponent_id, shell_holder_str, + width, height); + return nullptr; +} + +/** + * @brief xcomponent解除flutter引擎绑定 + * @note + * @param nativeShellHolderId: number + * @param xcomponentId: number + * @return napi_value + */ +napi_value PlatformViewOHOSNapi::nativeXComponentDetachFlutterEngine( + napi_env env, + napi_callback_info info) { + napi_status ret; + size_t argc = 2; + napi_value args[2] = {nullptr}; + std::string xcomponent_id; + int64_t shell_holder; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + FML_DLOG(ERROR) + << "nativeXComponentAttachFlutterEngine napi_get_cb_info error:" << ret; + return nullptr; + } + if (fml::napi::GetString(env, args[0], xcomponent_id) != 0) { + FML_DLOG(ERROR) + << "nativeXComponentAttachFlutterEngine xcomponent_id GetString error"; + return nullptr; + } + ret = napi_get_value_int64(env, args[1], &shell_holder); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeXComponentAttachFlutterEngine shell_holder " + "napi_get_value_int64 error"; + return nullptr; + } + + LOGD("nativeXComponentDetachFlutterEngine xcomponent_id: %{public}s", + xcomponent_id.c_str()); + XComponentAdapter::GetInstance()->DetachFlutterEngine(xcomponent_id); + return nullptr; +} + +/** + * @brief flutterEngine get mouseWheel event from ets + * @note + * @param nativeShellHolderId: number + * @param xcomponentId: number + * @param eventType: string + * @param fingerId: number + * @param globalX: number + * @param globalY: number + * @param offsetY: number + * @param timestamp: number + * @return napi_value + */ +napi_value PlatformViewOHOSNapi::nativeXComponentDispatchMouseWheel( + napi_env env, + napi_callback_info info) { + napi_status ret; + size_t argc = 8; + napi_value args[8] = {nullptr}; + int64_t shellHolder; + std::string xcomponentId; + std::string eventType; + int64_t fingerId; + double globalX; + double globalY; + double offsetY; + int64_t timestamp; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + FML_DLOG(ERROR) + << "nativeXComponentDispatchMouseWheel napi_get_cb_info error:" << ret; + return nullptr; + } + ret = napi_get_value_int64(env, args[0], &shellHolder); + if (ret != napi_ok) { + LOGE( + "nativeXComponentDispatchMouseWheel shellHolder napi_get_value_int64 " + "error"); + return nullptr; + } + if (fml::napi::GetString(env, args[1], xcomponentId) != 0) { + FML_DLOG(ERROR) + << "nativeXComponentDispatchMouseWheel xcomponentId GetString error"; + return nullptr; + } + if (fml::napi::GetString(env, args[2], eventType) != 0) { + FML_DLOG(ERROR) + << "nativeXComponentDispatchMouseWheel eventType GetString error"; + return nullptr; + } + ret = napi_get_value_int64(env, args[3], &fingerId); + if (ret != napi_ok) { + LOGE( + "nativeXComponentDispatchMouseWheel fingerId napi_get_value_int64 " + "error"); + return nullptr; + } + ret = napi_get_value_double(env, args[4], &globalX); + if (ret != napi_ok) { + LOGE( + "nativeXComponentDispatchMouseWheel globalX napi_get_value_double " + "error"); + return nullptr; + } + ret = napi_get_value_double(env, args[5], &globalY); + if (ret != napi_ok) { + LOGE( + "nativeXComponentDispatchMouseWheel globalY napi_get_value_double " + "error"); + return nullptr; + } + ret = napi_get_value_double(env, args[6], &offsetY); + if (ret != napi_ok) { + LOGE( + "nativeXComponentDispatchMouseWheel offsetY napi_get_value_double " + "error"); + return nullptr; + } + ret = napi_get_value_int64(env, args[7], ×tamp); + if (ret != napi_ok) { + LOGE( + "nativeXComponentDispatchMouseWheel timestamp napi_get_value_int64 " + "error"); + return nullptr; + } + flutter::mouseWheelEvent event{eventType, shellHolder, fingerId, globalX, + globalY, offsetY, timestamp}; + XComponentAdapter::GetInstance()->OnMouseWheel(xcomponentId, event); + return nullptr; +} + +/** + * @brief flutterEngine convert string to Uint8Array + * @note + * @param str: string + * @return napi_value + */ +napi_value PlatformViewOHOSNapi::nativeEncodeUtf8(napi_env env, + napi_callback_info info) { + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + size_t length = 0; + napi_get_value_string_utf8(env, args[0], nullptr, 0, &length); + + auto null_terminated_length = length + 1; + auto char_array = std::make_unique(null_terminated_length); + napi_get_value_string_utf8(env, args[0], char_array.get(), + null_terminated_length, nullptr); + + void* data; + napi_value arraybuffer; + napi_create_arraybuffer(env, length, &data, &arraybuffer); + std::memcpy(data, char_array.get(), length); + + napi_value uint8_array; + napi_create_typedarray(env, napi_uint8_array, length, arraybuffer, 0, + &uint8_array); + return uint8_array; +} + +/** + * @brief flutterEngine convert Uint8Array to string + * @note + * @param array: Uint8Array + * @return napi_value + */ +napi_value PlatformViewOHOSNapi::nativeDecodeUtf8(napi_env env, + napi_callback_info info) { + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + size_t size = 0; + void* data = nullptr; + napi_get_typedarray_info(env, args[0], nullptr, &size, &data, nullptr, + nullptr); + + napi_value result; + napi_create_string_utf8(env, static_cast(data), size, &result); + return result; +} + +/** + * 无障碍特征之字体加粗功能,获取ets侧系统字体粗细系数 + */ +napi_value PlatformViewOHOSNapi::nativeSetFontWeightScale( + napi_env env, + napi_callback_info info) { + napi_status ret; + size_t argc = 2; + napi_value args[2] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + // get param nativeShellHolderId + int64_t shell_holder; + ret = napi_get_value_int64(env, args[0], &shell_holder); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "PlatformViewOHOSNapi::nativeSetFontWeightScale " + "napi_get_value_int64 error:" + << ret; + return nullptr; + } + // get param fontWeightScale + double fontWeightScale = 1.0; + ret = napi_get_value_double(env, args[1], &fontWeightScale); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "PlatformViewOHOSNapi::nativeSetFontWeightScale " + "napi_get_value_double error:" + << ret; + return nullptr; + } + OHOS_SHELL_HOLDER->GetPlatformView()->SetBoldText(fontWeightScale); + FML_DLOG(INFO) + << "PlatformViewOHOSNapi::nativeSetFontWeightScale -> shell_holder: " + << shell_holder << " fontWeightScale: " << fontWeightScale; + return nullptr; +} + +napi_value PlatformViewOHOSNapi::nativeLookupCallbackInformation( + napi_env env, + napi_callback_info info) { + napi_value result; + size_t argc = 2; + napi_value args[2] = {nullptr}; + napi_status ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + LOGE("nativeLookupCallbackInformation napi_get_cb_info error"); + napi_create_int32(env, -1, &result); + return result; + } + + int64_t handle; + bool lossless; + ret = napi_get_value_bigint_int64(env, args[1], &handle, &lossless); + if (ret != napi_ok) { + LOGE("nativeLookupCallbackInformation napi_get_value_int64 error"); + napi_create_int32(env, -1, &result); + return result; + } + + LOGD("nativeLookupCallbackInformation::handle : %{public}ld", handle); + auto cbInfo = flutter::DartCallbackCache::GetCallbackInformation(handle); + if (cbInfo == nullptr) { + LOGE( + "nativeLookupCallbackInformation DartCallbackCache " + "GetCallbackInformation nullptr"); + napi_create_int32(env, -1, &result); + return result; + } + + napi_ref callbck_napi_obj; + ret = napi_create_reference(env, args[0], 1, &callbck_napi_obj); + if (ret != napi_ok) { + LOGE("nativeLookupCallbackInformation napi_create_reference error"); + napi_create_int32(env, -1, &result); + return result; + } + + napi_value callbackParam[3]; + napi_create_string_utf8(env, cbInfo->name.c_str(), NAPI_AUTO_LENGTH, + &callbackParam[0]); + napi_create_string_utf8(env, cbInfo->class_name.c_str(), NAPI_AUTO_LENGTH, + &callbackParam[1]); + napi_create_string_utf8(env, cbInfo->library_path.c_str(), NAPI_AUTO_LENGTH, + &callbackParam[2]); + + ret = fml::napi::InvokeJsMethod(env, callbck_napi_obj, "init", 3, + callbackParam); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeLookupCallbackInformation init fail "; + napi_create_int32(env, -1, &result); + return result; + } + napi_delete_reference(env, callbck_napi_obj); + napi_create_int32(env, 0, &result); + return result; +} + +napi_value PlatformViewOHOSNapi::nativeUnicodeIsEmoji(napi_env env, + napi_callback_info info) { + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + bool is_emoji = false; + int64_t codePoint = 0; + bool ret = napi_get_value_int64(env, args[0], &codePoint); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeXComponentAttachFlutterEngine shell_holder " + "napi_get_value_int64 error"; + return nullptr; + } + + is_emoji = u_hasBinaryProperty(codePoint, UProperty::UCHAR_EMOJI); + + napi_value result; + napi_create_int32(env, (int)is_emoji, &result); + return result; +} + +napi_value PlatformViewOHOSNapi::nativeUnicodeIsEmojiModifier( + napi_env env, + napi_callback_info info) { + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + bool is_emoji = false; + int64_t codePoint = 0; + bool ret = napi_get_value_int64(env, args[0], &codePoint); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeXComponentAttachFlutterEngine shell_holder " + "napi_get_value_int64 error"; + return nullptr; + } + + is_emoji = u_hasBinaryProperty(codePoint, UProperty::UCHAR_EMOJI_MODIFIER); + + napi_value result; + napi_create_int32(env, (int)is_emoji, &result); + return result; +} + +napi_value PlatformViewOHOSNapi::nativeUnicodeIsEmojiModifierBase( + napi_env env, + napi_callback_info info) { + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + bool is_emoji = false; + int64_t codePoint = 0; + bool ret = napi_get_value_int64(env, args[0], &codePoint); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeXComponentAttachFlutterEngine shell_holder " + "napi_get_value_int64 error"; + return nullptr; + } + + is_emoji = + u_hasBinaryProperty(codePoint, UProperty::UCHAR_EMOJI_MODIFIER_BASE); + + napi_value result; + napi_create_int32(env, (int)is_emoji, &result); + return result; +} + +napi_value PlatformViewOHOSNapi::nativeUnicodeIsVariationSelector( + napi_env env, + napi_callback_info info) { + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + bool is_emoji = false; + int64_t codePoint = 0; + bool ret = napi_get_value_int64(env, args[0], &codePoint); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeXComponentAttachFlutterEngine shell_holder " + "napi_get_value_int64 error"; + return nullptr; + } + + is_emoji = + u_hasBinaryProperty(codePoint, UProperty::UCHAR_VARIATION_SELECTOR); + + napi_value result; + napi_create_int32(env, (int)is_emoji, &result); + return result; +} + +napi_value PlatformViewOHOSNapi::nativeUnicodeIsRegionalIndicatorSymbol( + napi_env env, + napi_callback_info info) { + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + bool is_emoji = false; + int64_t codePoint = 0; + bool ret = napi_get_value_int64(env, args[0], &codePoint); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeXComponentAttachFlutterEngine shell_holder " + "napi_get_value_int64 error"; + return nullptr; + } + + is_emoji = + u_hasBinaryProperty(codePoint, UProperty::UCHAR_REGIONAL_INDICATOR); + + napi_value result; + napi_create_int32(env, (int)is_emoji, &result); + return result; +} + +/** + * 监听获取系统的无障碍服务是否开启 + */ +napi_value PlatformViewOHOSNapi::nativeAccessibilityStateChange( + napi_env env, + napi_callback_info info) { + size_t argc = 2; + napi_value args[2] = {nullptr}; + int64_t shell_holder; + bool state; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_bool(env, args[1], &state)); + + OHOS_SHELL_HOLDER->GetPlatformView()->OnAccessibilityStateChange(state); + + return nullptr; +} + +napi_value PlatformViewOHOSNapi::nativeAccessibilityAnnounce( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeAccessibilityAnnounce"; + size_t argc = 2; + napi_value args[2] = {nullptr}; + int64_t shell_holder; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + + size_t length = 0; + napi_get_value_string_utf8(env, args[1], nullptr, 0, &length); + + auto null_terminated_length = length + 1; + auto char_array = std::make_unique(null_terminated_length); + napi_get_value_string_utf8(env, args[1], char_array.get(), + null_terminated_length, nullptr); + + OHOS_SHELL_HOLDER->GetPlatformView()->AccessibilityAnnounce(char_array); + return nullptr; +} + +napi_value PlatformViewOHOSNapi::nativeAccessibilityOnTap( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeAccessibilityOnTap"; + size_t argc = 2; + napi_value args[2] = {nullptr}; + int64_t shell_holder; + int32_t nodeId; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_int32(env, args[1], &nodeId)); + + OHOS_SHELL_HOLDER->GetPlatformView()->AccessibilityOnTap(nodeId); + + return nullptr; +} + +napi_value PlatformViewOHOSNapi::nativeAccessibilityOnLongPress( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeAccessibilityOnTap"; + size_t argc = 2; + napi_value args[2] = {nullptr}; + int64_t shell_holder; + int32_t nodeId; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_int32(env, args[1], &nodeId)); + + OHOS_SHELL_HOLDER->GetPlatformView()->AccessibilityOnLongPress(nodeId); + return nullptr; +} + +napi_value PlatformViewOHOSNapi::nativeAccessibilityOnTooltip( + napi_env env, + napi_callback_info info) { + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeAccessibilityAnnounce"; + size_t argc = 2; + napi_value args[2] = {nullptr}; + int64_t shell_holder; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + + size_t length = 0; + napi_get_value_string_utf8(env, args[1], nullptr, 0, &length); + + auto null_terminated_length = length + 1; + auto char_array = std::make_unique(null_terminated_length); + napi_get_value_string_utf8(env, args[1], char_array.get(), + null_terminated_length, nullptr); + + OHOS_SHELL_HOLDER->GetPlatformView()->AccessibilityOnTooltip(char_array); + return nullptr; +} + +napi_value PlatformViewOHOSNapi::nativeSetSemanticsEnabled( + napi_env env, + napi_callback_info info) { + napi_status ret; + size_t argc = 2; + napi_value args[2] = {nullptr}; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "PlatformViewOHOSNapi::nativeSetSemanticsEnabled " + "napi_get_cb_info error:" + << ret; + return nullptr; + } + + int64_t shell_holder; + ret = napi_get_value_int64(env, args[0], &shell_holder); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "PlatformViewOHOSNapi::nativeSetSemanticsEnabled " + "napi_get_value_int64 error:" + << ret; + return nullptr; + } + bool enabled = false; + ret = napi_get_value_bool(env, args[1], &enabled); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "PlatformViewOHOSNapi::nativeSetSemanticsEnabled " + "napi_get_value_bool error:" + << ret; + return nullptr; + } + OHOS_SHELL_HOLDER->GetPlatformView()->SetSemanticsEnabled(enabled); + FML_DLOG(INFO) + << "PlatformViewOHOSNapi::nativeSetSemanticsEnabled " + "OHOS_SHELL_HOLDER->GetPlatformView()->SetSemanticsEnabled= " + << enabled; + + return nullptr; +} + +/** + * accessibility-relevant interfaces + */ +void PlatformViewOHOSNapi::SetSemanticsEnabled(int64_t shell_holder, + bool enabled) { + OHOS_SHELL_HOLDER->GetPlatformView()->SetSemanticsEnabled(enabled); +} + +void PlatformViewOHOSNapi::SetAccessibilityFeatures(int64_t shell_holder, + int32_t flags) { + OHOS_SHELL_HOLDER->GetPlatformView()->SetAccessibilityFeatures(flags); +} + +napi_value PlatformViewOHOSNapi::nativeSetFlutterNavigationAction( + napi_env env, + napi_callback_info info) { + size_t argc = 2; + napi_value args[2] = {nullptr}; + int64_t shell_holder; + bool isNavigate; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_CALL(env, napi_get_value_int64(env, args[0], &shell_holder)); + NAPI_CALL(env, napi_get_value_bool(env, args[1], &isNavigate)); + + OHOS_SHELL_HOLDER->GetPlatformView()->SetAccessibleNavigation(isNavigate); + FML_DLOG(INFO) << "PlatformViewOHOSNapi::nativeSetFlutterNavigationAction -> " + << isNavigate; + return nullptr; +} + +napi_value PlatformViewOHOSNapi::nativeUpdateCurrentXComponentId( + napi_env env, + napi_callback_info info) { + napi_status ret; + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + std::string xcomponent_id; + + if (fml::napi::GetString(env, args[0], xcomponent_id) != 0) { + FML_DLOG(ERROR) + << "nativeUpdateCurrentXComponentId xcomponent_id GetString error"; + return nullptr; + } + + std::lock_guard lock( + XComponentAdapter::GetInstance()->xcomponentMap_mutex_); + XComponentAdapter::GetInstance()->SetCurrentXcomponentId(xcomponent_id); + return nullptr; +} + +} // namespace flutter diff --git a/shell/platform/ohos/napi/platform_view_ohos_napi.h b/shell/platform/ohos/napi/platform_view_ohos_napi.h new file mode 100644 index 0000000000000000000000000000000000000000..79dc0e14ec979a40ee6917719cad04196b72671c --- /dev/null +++ b/shell/platform/ohos/napi/platform_view_ohos_napi.h @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_NAPI_PLATFORM_VIEW_OHOS_NAPI_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_NAPI_PLATFORM_VIEW_OHOS_NAPI_H_ +#include +#include "flutter/fml/file.h" +#include "flutter/fml/mapping.h" +#include "flutter/fml/task_runner.h" +#include "flutter/lib/ui/window/platform_message.h" +#include "napi/native_api.h" + +// class for all c++ to call js function +namespace flutter { + +struct locale { + std::string language; + std::string script; + std::string region; +}; + +struct mouseWheelEvent { + std::string eventType; + int64_t shellHolder; + int64_t fingerId; + double globalX; + double globalY; + double offsetY; + int64_t timestamp; +}; + +class PlatformViewOHOSNapi { + public: + static napi_value nativeDispatchEmptyPlatformMessage( + napi_env env, + napi_callback_info info); // 发送一个空的PlatformMessage + static napi_value nativeDispatchPlatformMessage( + napi_env env, + napi_callback_info info); // 发送一个PlatformMessage + static napi_value nativeInvokePlatformMessageEmptyResponseCallback( + napi_env env, + napi_callback_info info); // 空的PlatformMessage响应回调 + static napi_value nativeInvokePlatformMessageResponseCallback( + napi_env env, + napi_callback_info info); + + explicit PlatformViewOHOSNapi(napi_env env); + void SetPlatformTaskRunner(fml::RefPtr platform_task_runner); + void FlutterViewHandlePlatformMessageResponse( + int reponse_id, + std::unique_ptr data); + void FlutterViewHandlePlatformMessage( + int reponse_id, + std::unique_ptr message); + + void FlutterViewOnFirstFrame(bool is_preload = false); + void FlutterViewOnPreEngineRestart(); + flutter::locale resolveNativeLocale( + std::vector supportedLocales); + std::unique_ptr> + FlutterViewComputePlatformResolvedLocales( + const std::vector& support_locale_data); + + void FlutterViewOnTouchEvent(std::shared_ptr touchPacketString, + int size); + /** + * accessibility-relevant interfaces + */ + void SetSemanticsEnabled(int64_t shell_hoder, bool enabled); + void SetAccessibilityFeatures(int64_t shell_hoder, int32_t flags); + + static napi_value nativeUpdateRefreshRate( + napi_env env, + napi_callback_info info); // 设置刷新率 + static napi_value nativeUpdateSize(napi_env env, + napi_callback_info info); // 设置屏幕尺寸 + static napi_value nativeUpdateDensity( + napi_env env, + napi_callback_info info); // 设置屏幕像素密度(也就是缩放系数) + static napi_value nativeRunBundleAndSnapshotFromLibrary( + napi_env env, + napi_callback_info info); // 加载dart工程构建产物 + static napi_value nativePrefetchDefaultFontManager( + napi_env env, + napi_callback_info + info); // 初始化SkFontMgr::RefDefault(),skia引擎文字管理初始化 + static napi_value nativeCheckAndReloadFont( + napi_env env, + napi_callback_info info); // hot reload font + static napi_value nativeGetIsSoftwareRenderingEnabled( + napi_env env, + napi_callback_info info); // 返回是否支持软件绘制 + + static napi_value nativeAttach( + napi_env env, + napi_callback_info + info); // attach flutterNapi实例给到 native + // engine,这个支持rkts到flutter平台的无关引擎之间的通信 + // attach只需要执行一次 + static napi_value nativeSpawn( + napi_env env, + napi_callback_info info); // 从当前的flutterNapi复制一个新的实例 + static napi_value nativeDestroy( + napi_env env, + napi_callback_info info); // Detaches flutterNapi和engine之间的关联 + static napi_value nativeSetViewportMetrics( + napi_env env, + napi_callback_info info); // 把物理屏幕参数通知到native + static napi_value nativeSetAccessibilityFeatures( + napi_env env, + napi_callback_info info); // 设置能力参数 + + static napi_value nativeCleanupMessageData( + napi_env env, + napi_callback_info info); // 清除某个messageData + static napi_value nativeLoadDartDeferredLibrary( + napi_env env, + napi_callback_info info); // load一个合法的.so文件到dart vm + static napi_value nativeUpdateOhosAssetManager( + napi_env env, + napi_callback_info info); // 设置ResourceManager和assetBundlePath到engine + static napi_value nativeDeferredComponentInstallFailure( + napi_env env, + napi_callback_info info); // 加载动态库,或者dart库失败时的通知 + static napi_value nativeGetPixelMap( + napi_env env, + napi_callback_info info); // 从engine获取当前绘制pixelMap + static napi_value nativeNotifyLowMemoryWarning( + napi_env env, + napi_callback_info info); // 应用低内存警告 + + // 下面的方法,从键盘输入中判断当前字符是否是emoji,实现优先级低 + static napi_value nativeFlutterTextUtilsIsEmoji(napi_env env, + napi_callback_info info); + static napi_value nativeFlutterTextUtilsIsEmojiModifier( + napi_env env, + napi_callback_info info); + static napi_value nativeFlutterTextUtilsIsEmojiModifierBase( + napi_env env, + napi_callback_info info); + static napi_value nativeFlutterTextUtilsIsVariationSelector( + napi_env env, + napi_callback_info info); + static napi_value nativeFlutterTextUtilsIsRegionalIndicator( + napi_env env, + napi_callback_info info); + static napi_value nativeGetSystemLanguages( + napi_env env, + napi_callback_info info); // 应用下发系统语言设置 + + static napi_value nativeUnregisterTexture(napi_env env, + napi_callback_info info); + + static napi_value nativeMarkTextureFrameAvailable(napi_env env, + napi_callback_info info); + + static napi_value nativeRegisterPixelMap(napi_env env, + napi_callback_info info); + + static napi_value nativeRegisterTexture(napi_env env, + napi_callback_info info); + + static napi_value nativeGetTextureWindowId(napi_env env, + napi_callback_info info); + static napi_value nativeSetTextureBackGroundPixelMap(napi_env env, + napi_callback_info info); + + static napi_value nativeSetTextureBufferSize(napi_env env, + napi_callback_info info); + + static napi_value nativeNotifyTextureResizing(napi_env env, + napi_callback_info info); + + static napi_value nativeSetExternalNativeImage(napi_env env, + napi_callback_info info); + + static napi_value nativeResetExternalTexture(napi_env env, + napi_callback_info info); + + static napi_value nativeEnableFrameCache(napi_env env, + napi_callback_info info); + + // Surface相关,XComponent调用 + static void SurfaceCreated(int64_t shell_holder, + void* window, + int width, + int height); + + static void SurfacePreload(int64_t shell_holder, int width, int height); + + static void SurfaceChanged(int64_t shell_holder, + void* window, + int width, + int height); + + static void SurfaceDestroyed(int64_t shell_holder); + + static napi_value nativeXComponentAttachFlutterEngine( + napi_env env, + napi_callback_info info); + static napi_value nativeXComponentDetachFlutterEngine( + napi_env env, + napi_callback_info info); + static napi_value nativeXComponentPreDraw(napi_env env, + napi_callback_info info); + + static int64_t display_width; + static int64_t display_height; + static int64_t display_refresh_rate; + static double display_density_pixels; + static napi_value nativeXComponentDispatchMouseWheel(napi_env env, + napi_callback_info info); + static napi_value nativeEncodeUtf8(napi_env env, napi_callback_info info); + static napi_value nativeDecodeUtf8(napi_env env, napi_callback_info info); + static napi_value nativeLookupCallbackInformation(napi_env env, + napi_callback_info info); + + static napi_value nativeUnicodeIsEmoji(napi_env env, napi_callback_info info); + + static napi_value nativeUnicodeIsEmojiModifier(napi_env env, + napi_callback_info info); + + static napi_value nativeUnicodeIsEmojiModifierBase(napi_env env, + napi_callback_info info); + + static napi_value nativeUnicodeIsVariationSelector(napi_env env, + napi_callback_info info); + + static napi_value nativeUnicodeIsRegionalIndicatorSymbol( + napi_env env, + napi_callback_info info); + + /** + * ets call c++ + */ + static napi_value nativeAccessibilityStateChange(napi_env env, + napi_callback_info info); + static napi_value nativeAccessibilityAnnounce(napi_env env, + napi_callback_info info); + static napi_value nativeAccessibilityOnTap(napi_env env, + napi_callback_info info); + static napi_value nativeAccessibilityOnLongPress(napi_env env, + napi_callback_info info); + static napi_value nativeAccessibilityOnTooltip(napi_env env, + napi_callback_info info); + static napi_value nativeSetSemanticsEnabled(napi_env env, + napi_callback_info info); + static napi_value nativeSetFlutterNavigationAction(napi_env env, + napi_callback_info info); + + static napi_value nativeSetFontWeightScale(napi_env env, + napi_callback_info info); + + static napi_value nativeUpdateCurrentXComponentId(napi_env env, + napi_callback_info info); + + private: + static napi_env env_; + napi_ref ref_napi_obj_; + static std::vector system_languages; + fml::RefPtr platform_task_runner_; + static int64_t napi_shell_holder_id_; +}; + +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_NAPI_PLATFORM_VIEW_OHOS_NAPI_H_ diff --git a/shell/platform/ohos/napi_common.h b/shell/platform/ohos/napi_common.h new file mode 100644 index 0000000000000000000000000000000000000000..037bd62a784726a157929a235533765282199091 --- /dev/null +++ b/shell/platform/ohos/napi_common.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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. + */ + +// +// Created on 2023/6/17. +// +// Node APIs are not fully supported. To solve the compilation error of the +// interface cannot be found, please include "napi/native_api.h". + +#ifndef FLUTTER_SHELL_PLATFORM_OHOS_NAPI_COMMON_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_NAPI_COMMON_H_ + +#define NAPI_RETVAL_NOTHING + +#define GET_AND_THROW_LAST_ERROR(env) \ + do { \ + const napi_extended_error_info* errorInfo = nullptr; \ + napi_get_last_error_info((env), &errorInfo); \ + bool isPending = false; \ + napi_is_exception_pending((env), &isPending); \ + if (!isPending && errorInfo != nullptr) { \ + const char* errorMessage = errorInfo->error_message != nullptr \ + ? errorInfo->error_message \ + : "empty error message"; \ + napi_throw_error((env), nullptr, errorMessage); \ + } \ + } while (0) + +#define NAPI_ASSERT_BASE(env, assertion, message, retVal) \ + do { \ + if (!(assertion)) { \ + napi_throw_error((env), nullptr, \ + "assertion (" #assertion ") failed: " message); \ + return retVal; \ + } \ + } while (0) + +#define NAPI_ASSERT(env, assertion, message) \ + NAPI_ASSERT_BASE(env, assertion, message, nullptr) + +#define NAPI_ASSERT_RETURN_VOID(env, assertion, message) \ + NAPI_ASSERT_BASE(env, assertion, message, NAPI_RETVAL_NOTHING) + +#define NAPI_CALL_BASE(env, theCall, retVal) \ + do { \ + if ((theCall) != napi_ok) { \ + GET_AND_THROW_LAST_ERROR((env)); \ + return retVal; \ + } \ + } while (0) + +#define NAPI_CALL(env, theCall) NAPI_CALL_BASE(env, theCall, nullptr) + +#define NAPI_CALL_RETURN_VOID(env, theCall) \ + NAPI_CALL_BASE(env, theCall, NAPI_RETVAL_NOTHING) + +#define DECLARE_NAPI_PROPERTY(name, val) \ + { (name), nullptr, nullptr, nullptr, nullptr, val, napi_default, nullptr } + +#define DECLARE_NAPI_STATIC_PROPERTY(name, val) \ + { (name), nullptr, nullptr, nullptr, nullptr, val, napi_static, nullptr } + +#define DECLARE_NAPI_FUNCTION(name, func) \ + { (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, nullptr } + +#define DECLARE_NAPI_FUNCTION_WITH_DATA(name, func, data) \ + { (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, data } + +#define DECLARE_NAPI_STATIC_FUNCTION(name, func) \ + { (name), nullptr, (func), nullptr, nullptr, nullptr, napi_static, nullptr } + +#define DECLARE_NAPI_GETTER(name, getter) \ + { \ + (name), nullptr, nullptr, (getter), nullptr, nullptr, napi_default, \ + nullptr \ + } + +#define DECLARE_NAPI_SETTER(name, setter) \ + { \ + (name), nullptr, nullptr, nullptr, (setter), nullptr, napi_default, \ + nullptr \ + } + +#define DECLARE_NAPI_GETTER_SETTER(name, getter, setter) \ + { \ + (name), nullptr, nullptr, (getter), (setter), nullptr, napi_default, \ + nullptr \ + } + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_NAPI_COMMON_H_ diff --git a/shell/platform/ohos/ohos_asset_provider.cpp b/shell/platform/ohos/ohos_asset_provider.cpp new file mode 100644 index 0000000000000000000000000000000000000000..814dcc383fc9c1a8cae9e7047b15995132cf95e9 --- /dev/null +++ b/shell/platform/ohos/ohos_asset_provider.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "ohos_asset_provider.h" +#include +#include +#include "napi_common.h" +#include "ohos_logging.h" + +namespace flutter { + +class FileDescriptionMapping : public fml::Mapping { + public: + explicit FileDescriptionMapping(RawFile* fileHandle) + : file_handle_(fileHandle) { + LOGD("FileDescriptionMapping :%{public}p", file_handle_); + int ret = 0; + if (0 > (ret = ReadFile())) { + LOGD("FileDescriptionMapping :%{public}d", ret); + } + } + void* GetBuffer() { + // TODO 考虑线程安全 + + if (file_buf_ != nullptr) + return file_buf_; + + size_t bufLenth = GetSize(); + + if (file_handle_ != nullptr && bufLenth > 0) { + LOGD("FileDescriptionMapping buflenth = %{public}ld", bufLenth); + file_buf_ = malloc(bufLenth + 1); + memset(file_buf_, 0, bufLenth + 1); + } + return file_buf_; + } + + ~FileDescriptionMapping() override { + if (file_handle_ != nullptr) { + OH_ResourceManager_CloseRawFile(file_handle_); + } + if (file_buf_ != nullptr) { + free(file_buf_); + file_buf_ = nullptr; + } + } + + size_t GetSize() const override { + if (file_handle_ == nullptr) + return 0; + + size_t ret = OH_ResourceManager_GetRawFileSize(file_handle_); + LOGD("GetSize():%{public}zu", ret); + return ret; + } + + int ReadFile() { + if (file_handle_ == nullptr) { + LOGE("GetMapping failed. fileHandle_:%{public}p,filebuf:%{public}p", + file_handle_, file_buf_); + return -1; + } + + size_t len = GetSize(); + void* buf = GetBuffer(); + int ret = 0; + + if (buf != nullptr) { + ret = OH_ResourceManager_ReadRawFile(file_handle_, buf, len); + read_from_file_ = true; + } + LOGD("GetMapping ... total:%{public}zu, ret:%{public}d,buf:%{public}p", len, + ret, buf); + return ret; + } + const uint8_t* GetMapping() const override { + return reinterpret_cast(file_buf_); + } + + bool IsDontNeedSafe() const override { + // thread unsafe + return false; + } + + private: + RawFile* file_handle_; + void* file_buf_ = nullptr; + bool read_from_file_ = false; + FML_DISALLOW_COPY_AND_ASSIGN(FileDescriptionMapping); +}; + +OHOSAssetProvider::OHOSAssetProvider(void* assetHandle, const std::string& dir) + : asset_handle_(assetHandle), dir_(dir) { + LOGD("assets dir:%{public}s", dir.c_str()); +} + +OHOSAssetProvider::OHOSAssetProvider( + std::shared_ptr handle) + : asset_handle_(handle.get()) {} + +bool OHOSAssetProvider::IsValid() const { + return (asset_handle_ != nullptr); +} + +bool OHOSAssetProvider::IsValidAfterAssetManagerChange() const { + return true; +} + +AssetResolver::AssetResolverType OHOSAssetProvider::GetType() const { + return AssetResolver::AssetResolverType::kApkAssetProvider; +} + +std::unique_ptr OHOSAssetProvider::GetAsMapping( + const std::string& asset_name) const { + NativeResourceManager* nativeResMgr = + reinterpret_cast(asset_handle_); + if (asset_handle_ == nullptr || nativeResMgr == nullptr) { + LOGE("nativeResMgr is null:%{public}p or nativeResMgr is null:%{public}p ", + asset_handle_, nativeResMgr); + } + std::string relativePath = dir_ + "/" + asset_name; + + LOGD("GetAsMapping=%{public}s->%{public}s", asset_name.c_str(), + relativePath.c_str()); + + RawFile* fileHandle = + OH_ResourceManager_OpenRawFile(nativeResMgr, relativePath.c_str()); + LOGD("GetAsMapping=%{public}s->%{public}p", relativePath.c_str(), fileHandle); + + if (fileHandle == nullptr) { + fileHandle = + OH_ResourceManager_OpenRawFile(nativeResMgr, asset_name.c_str()); + LOGD("GetAsMapping2 ..fallback:%{public}s->%{public}p", asset_name.c_str(), + fileHandle); + } + LOGD("GetAsMappingend:%{public}p", fileHandle); + return fileHandle == nullptr + ? nullptr + : (std::make_unique(fileHandle)); +} + +std::unique_ptr OHOSAssetProvider::Clone() const { + return std::make_unique(asset_handle_); +} + +bool OHOSAssetProvider::operator==(const AssetResolver& other) const { + auto other_provider = other.as_ohos_asset_provider(); + if (!other_provider) { + return false; + } + return asset_handle_ == other_provider->asset_handle_; +} + +} // namespace flutter diff --git a/shell/platform/ohos/ohos_asset_provider.h b/shell/platform/ohos/ohos_asset_provider.h new file mode 100755 index 0000000000000000000000000000000000000000..4c8a4a1549ef82e828cb8e12eed522cf7a9b1c51 --- /dev/null +++ b/shell/platform/ohos/ohos_asset_provider.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_ASSET_PROVIDER_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_ASSET_PROVIDER_H_ + +#include "flutter/assets/asset_resolver.h" +#include "flutter/fml/memory/ref_counted.h" + +namespace flutter { + +class OHOSAssetProviderInternal { + public: + virtual std::unique_ptr GetAsMapping( + const std::string& asset_name) const = 0; + protected: + virtual ~OHOSAssetProviderInternal() = default; +}; + +// ohos平台的文件管理 ,必须通过NativeResourceManager* 指针对它进行初始化 +class OHOSAssetProvider final : public AssetResolver { + public: + explicit OHOSAssetProvider(void* assetHandle, + const std::string& dir = "flutter_assets"); + + explicit OHOSAssetProvider(std::shared_ptr assetHandle); + + ~OHOSAssetProvider() = default; + + std::unique_ptr Clone() const; + + void* GetHandle() const { return asset_handle_; } + + bool operator==(const AssetResolver& other) const override; + + private: + void* asset_handle_; + std::string dir_; + + bool IsValid() const override; + + bool IsValidAfterAssetManagerChange() const override; + + AssetResolver::AssetResolverType GetType() const override; + + std::unique_ptr GetAsMapping( + const std::string& asset_name) const override; + + // |AssetResolver| + const OHOSAssetProvider* as_ohos_asset_provider() const override { + return this; + } + + FML_DISALLOW_COPY_AND_ASSIGN(OHOSAssetProvider); +}; +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_ASSET_PROVIDER_H_ \ No newline at end of file diff --git a/shell/platform/ohos/ohos_asset_provider_unittests.cpp b/shell/platform/ohos/ohos_asset_provider_unittests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e177beba8acfbcd05f3b3e70f70e91ea6e34fac9 --- /dev/null +++ b/shell/platform/ohos/ohos_asset_provider_unittests.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/ohos_asset_provider.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +class MockOHOSAssetProviderImpl : public OHOSAssetProviderInternal { + public: + MOCK_METHOD(std::unique_ptr, + GetAsMapping, + (const std::string& asset_name), + (const, override)); +}; + +TEST(OHOSAssetProvider, CloneAndEquals) { + auto first_impl = std::make_shared(); + auto second_impl = std::make_shared(); + auto first_provider = std::make_unique(first_impl); + auto second_provider = std::make_unique(second_impl); + auto third_provider = first_provider->Clone(); + + ASSERT_NE(first_provider->GetHandle(), second_provider->GetHandle()); + ASSERT_EQ(first_provider->GetHandle(), third_provider->GetHandle()); + ASSERT_FALSE(*first_provider == *second_provider); + ASSERT_TRUE(*first_provider == *third_provider); +} +} // namespace testing +} // namespace flutter diff --git a/shell/platform/ohos/ohos_context_gl_impeller.cpp b/shell/platform/ohos/ohos_context_gl_impeller.cpp new file mode 100755 index 0000000000000000000000000000000000000000..1610073bb8364a0d9f55134dd5be5de8a85770d7 --- /dev/null +++ b/shell/platform/ohos/ohos_context_gl_impeller.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "ohos_context_gl_impeller.h" + +namespace flutter { + +OHOSContextGLImpeller::OHOSContextGLImpeller() + : OHOSContext(OHOSRenderingAPI::kOpenGLES) {} + +OHOSContextGLImpeller::~OHOSContextGLImpeller() {} + +bool OHOSContextGLImpeller::IsValid() const { + return true; +} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/ohos_context_gl_impeller.h b/shell/platform/ohos/ohos_context_gl_impeller.h new file mode 100755 index 0000000000000000000000000000000000000000..7851bb64b965ecb77bbf581ea8bbb30ad53b9d0a --- /dev/null +++ b/shell/platform/ohos/ohos_context_gl_impeller.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_CONTEXT_GL_IMPELLER_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_CONTEXT_GL_IMPELLER_H_ +#include "context/ohos_context.h" +#include "flutter/fml/macros.h" + +namespace flutter { +class OHOSContextGLImpeller : public OHOSContext { + public: + OHOSContextGLImpeller(); + + ~OHOSContextGLImpeller() override; + + bool IsValid() const override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(OHOSContextGLImpeller); +}; +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_CONTEXT_GL_IMPELLER_H_ \ No newline at end of file diff --git a/shell/platform/ohos/ohos_context_gl_skia.cpp b/shell/platform/ohos/ohos_context_gl_skia.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b5bade6279c0df964bb001a998fe9dab42651aa9 --- /dev/null +++ b/shell/platform/ohos/ohos_context_gl_skia.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/ohos_context_gl_skia.h" + +#include + +#include "flutter/fml/trace_event.h" +#include "flutter/shell/platform/ohos/ohos_egl_surface.h" + +namespace flutter { + +template +using EGLResult = std::pair; + +static EGLResult CreateContext(EGLDisplay display, + EGLConfig config, + EGLContext share = EGL_NO_CONTEXT) { + EGLint attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; + + EGLContext context = eglCreateContext(display, config, share, attributes); + + return {context != EGL_NO_CONTEXT, context}; +} + +static EGLResult ChooseEGLConfiguration(EGLDisplay display, + uint8_t msaa_samples) { + EGLint sample_buffers = msaa_samples > 1 ? 1 : 0; + EGLint attributes[] = { + // clang-format off + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 0, + EGL_STENCIL_SIZE, 0, + EGL_SAMPLES, static_cast(msaa_samples), + EGL_SAMPLE_BUFFERS, sample_buffers, + EGL_NONE, // termination sentinel + // clang-format on + }; + + EGLint config_count = 0; + EGLConfig egl_config = nullptr; + + if (eglChooseConfig(display, attributes, &egl_config, 1, &config_count) != + EGL_TRUE) { + return {false, nullptr}; + } + + bool success = config_count > 0 && egl_config != nullptr; + + return {success, success ? egl_config : nullptr}; +} + +static bool TeardownContext(EGLDisplay display, EGLContext context) { + if (context != EGL_NO_CONTEXT) { + return eglDestroyContext(display, context) == EGL_TRUE; + } + + return true; +} + +OhosContextGLSkia::OhosContextGLSkia(OHOSRenderingAPI rendering_api, + const TaskRunners& task_runners, + uint8_t msaa_samples) + : OHOSContext(OHOSRenderingAPI::kOpenGLES), + config_(nullptr), + task_runners_(task_runners) { + environment_ = fml::MakeRefCounted(); + if (!environment_->IsValid()) { + FML_LOG(ERROR) << "Could not create an Ohos GL environment."; + return; + } + + bool success = false; + + TRACE_EVENT0("flutter", "OhosContextGLSkia"); + + // Choose a valid configuration. + std::tie(success, config_) = + ChooseEGLConfiguration(environment_->Display(), msaa_samples); + if (!success) { + FML_LOG(ERROR) << "Could not choose an EGL configuration."; + LogLastEGLError(); + return; + } + FML_LOG(INFO) << "create gl context"; + // Create a context for the configuration. + std::tie(success, context_) = + CreateContext(environment_->Display(), config_, EGL_NO_CONTEXT); + if (!success) { + FML_LOG(ERROR) << "Could not create an EGL context"; + LogLastEGLError(); + return; + } + + std::tie(success, resource_context_) = + CreateContext(environment_->Display(), config_, context_); + if (!success) { + FML_LOG(ERROR) << "Could not create an EGL resource context"; + LogLastEGLError(); + return; + } + + // All done! + valid_ = true; +} + +OhosContextGLSkia::~OhosContextGLSkia() { + FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + sk_sp main_context = GetMainSkiaContext(); + SetMainSkiaContext(nullptr); + fml::AutoResetWaitableEvent latch; + // This context needs to be deallocated from the raster thread in order to + // keep a coherent usage of egl from a single thread. + fml::TaskRunner::RunNowOrPostTask(task_runners_.GetRasterTaskRunner(), [&] { + if (main_context) { + std::unique_ptr pbuffer_surface = CreatePbufferSurface(); + auto status = pbuffer_surface->MakeCurrent(); + if (status != OhosEGLSurfaceMakeCurrentStatus::kFailure) { + main_context->releaseResourcesAndAbandonContext(); + main_context.reset(); + ClearCurrent(); + } + } + latch.Signal(); + }); + latch.Wait(); + + if (!TeardownContext(environment_->Display(), context_)) { + FML_LOG(ERROR) + << "Could not tear down the EGL context. Possible resource leak."; + LogLastEGLError(); + } + + if (!TeardownContext(environment_->Display(), resource_context_)) { + FML_LOG(ERROR) << "Could not tear down the EGL resource context. Possible " + "resource leak."; + LogLastEGLError(); + } +} + +std::unique_ptr OhosContextGLSkia::CreateOnscreenSurface( + const fml::RefPtr& window) const { + if (window->IsFakeWindow()) { + return CreatePbufferSurface(); + } else { + EGLDisplay display = environment_->Display(); + + const EGLint attribs[] = {EGL_NONE}; + + EGLSurface surface = eglCreateWindowSurface( + display, config_, + reinterpret_cast(window->handle()), attribs); + FML_LOG(INFO) << "create EGLSurface " << eglGetError() << " " << surface + << " " << context_; + return std::make_unique(surface, display, context_); + } +} + +std::unique_ptr OhosContextGLSkia::CreateOffscreenSurface() + const { + // We only ever create pbuffer surfaces for background resource loading + // contexts. We never bind the pbuffer to anything. + FML_LOG(INFO) << "CreateOffscreenSurface 1"; + if (environment_) { + FML_LOG(INFO) << "CreateOffscreenSurface environment_ "; + } else { + FML_LOG(INFO) << "CreateOffscreenSurface environment_ nullptr"; + } + + EGLDisplay display = environment_->Display(); + + const EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; + FML_LOG(INFO) << "CreateOffscreenSurface 2"; + EGLSurface surface = eglCreatePbufferSurface(display, config_, attribs); + FML_LOG(INFO) << "CreateOffscreenSurface 3"; + return std::make_unique(surface, display, resource_context_); +} + +std::unique_ptr OhosContextGLSkia::CreatePbufferSurface( + int width, + int height) const { + EGLDisplay display = environment_->Display(); + + const EGLint attribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE}; + + FML_LOG(INFO) << "CreatePbufferSurface"; + EGLSurface surface = eglCreatePbufferSurface(display, config_, attribs); + return std::make_unique(surface, display, context_); +} + +fml::RefPtr OhosContextGLSkia::Environment() const { + return environment_; +} + +bool OhosContextGLSkia::IsValid() const { + return valid_; +} + +bool OhosContextGLSkia::ClearCurrent() const { + if (eglGetCurrentContext() != context_) { + return true; + } + if (eglMakeCurrent(environment_->Display(), EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT) != EGL_TRUE) { + FML_LOG(ERROR) << "Could not clear the current context"; + LogLastEGLError(); + return false; + } + return true; +} + +EGLContext OhosContextGLSkia::CreateNewContext() const { + bool success; + EGLContext context; + std::tie(success, context) = + CreateContext(environment_->Display(), config_, EGL_NO_CONTEXT); + return success ? context : EGL_NO_CONTEXT; +} + +} // namespace flutter diff --git a/shell/platform/ohos/ohos_context_gl_skia.h b/shell/platform/ohos/ohos_context_gl_skia.h new file mode 100644 index 0000000000000000000000000000000000000000..5b15b7a630e68b6531aa3d315b2ccb659eeefb36 --- /dev/null +++ b/shell/platform/ohos/ohos_context_gl_skia.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_CONTEXT_GL_SKIA_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_CONTEXT_GL_SKIA_H_ + +#include "flutter/fml/macros.h" +#include "flutter/fml/memory/ref_counted.h" +#include "flutter/fml/memory/ref_ptr.h" +#include "flutter/shell/common/platform_view.h" +#include "flutter/shell/platform/ohos/context/ohos_context.h" +#include "flutter/shell/platform/ohos/ohos_environment_gl.h" +#include "flutter/shell/platform/ohos/surface/ohos_native_window.h" +#include "third_party/skia/include/core/SkSize.h" + +namespace flutter { + +class OhosEGLSurface; + +//------------------------------------------------------------------------------ +/// The Ohos context is used by `OhosSurfaceGL` to create and manage +/// EGL surfaces. +/// +/// This context binds `EGLContext` to the current rendering thread and to the +/// draw and read `EGLSurface`s. +/// +class OhosContextGLSkia : public OHOSContext { + public: + OhosContextGLSkia(OHOSRenderingAPI rendering_api, + const TaskRunners& taskRunners, + uint8_t msaa_samples); + + ~OhosContextGLSkia(); + + //---------------------------------------------------------------------------- + /// @brief Allocates an new EGL window surface that is used for on-screen + /// pixels. + /// + /// @return The window surface. + /// + std::unique_ptr CreateOnscreenSurface( + const fml::RefPtr& window) const; + + //---------------------------------------------------------------------------- + /// @brief Allocates an 1x1 pbuffer surface that is used for making the + /// offscreen current for texture uploads. + /// + /// @return The pbuffer surface. + /// + std::unique_ptr CreateOffscreenSurface() const; + + //---------------------------------------------------------------------------- + /// @brief Allocates an 1x1 pbuffer surface that is used for making the + /// onscreen context current for snapshotting. + /// + /// @return The pbuffer surface. + /// + std::unique_ptr CreatePbufferSurface(int width = 1, + int height = 1) const; + + //---------------------------------------------------------------------------- + /// @return The Ohos environment that contains a reference to the + /// display. + /// + fml::RefPtr Environment() const; + + //---------------------------------------------------------------------------- + /// @return Whether the current context is valid. That is, if the EGL + /// contexts were successfully created. + /// + bool IsValid() const override; + + //---------------------------------------------------------------------------- + /// @return Whether the current context was successfully clear. + /// + bool ClearCurrent() const; + + //---------------------------------------------------------------------------- + /// @brief Create a new EGLContext using the same EGLConfig. + /// + /// @return The EGLContext. + /// + EGLContext CreateNewContext() const; + + //---------------------------------------------------------------------------- + /// @brief The EGLConfig for this context. + /// + EGLConfig Config() const { return config_; } + + private: + fml::RefPtr environment_; + EGLConfig config_; + EGLContext context_; + EGLContext resource_context_; + bool valid_ = false; + TaskRunners task_runners_; + + FML_DISALLOW_COPY_AND_ASSIGN(OhosContextGLSkia); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_CONTEXT_GL_SKIA_H_ diff --git a/shell/platform/ohos/ohos_context_vulkan_impeller.cpp b/shell/platform/ohos/ohos_context_vulkan_impeller.cpp new file mode 100644 index 0000000000000000000000000000000000000000..41365b0acd4c7bf8fbbdce35e33b41107257e0c7 --- /dev/null +++ b/shell/platform/ohos/ohos_context_vulkan_impeller.cpp @@ -0,0 +1,92 @@ +// Copyright 2013 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. + +#include "flutter/shell/platform/ohos/ohos_context_vulkan_impeller.h" + +#include "flutter/fml/paths.h" +#include "flutter/impeller/entity/vk/entity_shaders_vk.h" +#include "flutter/impeller/entity/vk/framebuffer_blend_shaders_vk.h" +#include "flutter/impeller/entity/vk/modern_shaders_vk.h" +#include "flutter/impeller/renderer/backend/vulkan/context_vk.h" +#include "shell/platform/ohos/context/ohos_context.h" + +#if IMPELLER_ENABLE_3D +#include "flutter/impeller/scene/shaders/vk/scene_shaders_vk.h" // nogncheck +#endif // IMPELLER_ENABLE_3D + +namespace flutter { + +static std::shared_ptr CreateImpellerContext( + const fml::RefPtr& vulkan_dylib, + bool enable_vulkan_validation, + bool enable_gpu_tracing, + bool quiet) { + if (!vulkan_dylib) { + VALIDATION_LOG << "Could not open the Vulkan dylib."; + return nullptr; + } + + std::vector> shader_mappings = { + std::make_shared(impeller_entity_shaders_vk_data, + impeller_entity_shaders_vk_length), + std::make_shared( + impeller_framebuffer_blend_shaders_vk_data, + impeller_framebuffer_blend_shaders_vk_length), +#if IMPELLER_ENABLE_3D + std::make_shared(impeller_scene_shaders_vk_data, + impeller_scene_shaders_vk_length), +#endif + std::make_shared(impeller_modern_shaders_vk_data, + impeller_modern_shaders_vk_length), + }; + + auto instance_proc_addr = + vulkan_dylib->ResolveFunction( + "vkGetInstanceProcAddr"); + + if (!instance_proc_addr.has_value()) { + VALIDATION_LOG << "Could not setup Vulkan proc table."; + return nullptr; + } + + impeller::ContextVK::Settings settings; + settings.proc_address_callback = instance_proc_addr.value(); + settings.shader_libraries_data = std::move(shader_mappings); + settings.cache_directory = fml::paths::GetCachesDirectory(); + settings.enable_validation = enable_vulkan_validation; + settings.enable_gpu_tracing = enable_gpu_tracing; + + auto context = impeller::ContextVK::Create(std::move(settings)); + + if (!quiet) { + if (context && impeller::CapabilitiesVK::Cast(*context->GetCapabilities()) + .AreValidationsEnabled()) { + FML_LOG(INFO) << "Using the Impeller rendering backend (Vulkan with " + "Validation Layers)."; + } else { + FML_LOG(INFO) << "Using the Impeller rendering backend (Vulkan)."; + } + } + + return context; +} + +OHOSContextVulkanImpeller::OHOSContextVulkanImpeller(bool enable_validation, + bool enable_gpu_tracing, + bool quiet) + : OHOSContext(OHOSRenderingAPI::kImpellerVulkan) { + vulkan_dylib_ = fml::NativeLibrary::Create("libvulkan.so"); + auto impeller_context = CreateImpellerContext( + vulkan_dylib_, enable_validation, enable_gpu_tracing, quiet); + SetImpellerContext(impeller_context); + is_valid_ = !!impeller_context; +} + +OHOSContextVulkanImpeller::~OHOSContextVulkanImpeller() = default; + +bool OHOSContextVulkanImpeller::IsValid() const { + return is_valid_; +} + +} // namespace flutter diff --git a/shell/platform/ohos/ohos_context_vulkan_impeller.h b/shell/platform/ohos/ohos_context_vulkan_impeller.h new file mode 100644 index 0000000000000000000000000000000000000000..01512a505c52aaaa49b09faeacc0317556423297 --- /dev/null +++ b/shell/platform/ohos/ohos_context_vulkan_impeller.h @@ -0,0 +1,33 @@ +// Copyright 2013 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. + +#ifndef FLUTTER_SHELL_PLATFORM_OHOS_OHOS_CONTEXT_VULKAN_IMPELLER_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_CONTEXT_VULKAN_IMPELLER_H_ + +#include "flutter/fml/macros.h" +#include "flutter/fml/native_library.h" +#include "flutter/shell/platform/ohos/context/ohos_context.h" + +namespace flutter { + +class OHOSContextVulkanImpeller : public OHOSContext { + public: + OHOSContextVulkanImpeller(bool enable_validation, + bool enable_gpu_tracing, + bool quiet = false); + + ~OHOSContextVulkanImpeller(); + + // |OHOSContext| + bool IsValid() const override; + + private: + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(OHOSContextVulkanImpeller); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_CONTEXT_VULKAN_IMPELLER_H_ diff --git a/shell/platform/ohos/ohos_display.cpp b/shell/platform/ohos/ohos_display.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5813baf58483a745ee8d844e9a7633c01bb4c53f --- /dev/null +++ b/shell/platform/ohos/ohos_display.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/ohos_display.h" + +namespace flutter { + +OHOSDisplay::OHOSDisplay(std::shared_ptr napi_facade) + : Display(0, + napi_facade_->display_refresh_rate, + napi_facade_->display_width, + napi_facade_->display_height, + napi_facade_->display_density_pixels), + napi_facade_(std::move(napi_facade)) {} + +double OHOSDisplay::GetRefreshRate() const { + return (double)napi_facade_->display_refresh_rate; +} + +double OHOSDisplay::GetWidth() const { + return (double)napi_facade_->display_width; +} + +double OHOSDisplay::GetHeight() const { + return (double)napi_facade_->display_height; +} + +double OHOSDisplay::GetDevicePixelRatio() const { + return napi_facade_->display_density_pixels; +} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/ohos_display.h b/shell/platform/ohos/ohos_display.h new file mode 100644 index 0000000000000000000000000000000000000000..84a003614a8fb190072ca71574ff3ae870524297 --- /dev/null +++ b/shell/platform/ohos/ohos_display.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_DISPLAY_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_DISPLAY_H_ + +#include + +#include "flutter/fml/macros.h" +#include "flutter/shell/common/display.h" +#include "flutter/shell/platform/ohos/napi/platform_view_ohos_napi.h" +namespace flutter { + +class OHOSDisplay : public Display { + public: + explicit OHOSDisplay(std::shared_ptr napi_facade_); + ~OHOSDisplay() = default; + + double GetRefreshRate() const override; + + // |Display| + virtual double GetWidth() const override; + + // |Display| + virtual double GetHeight() const override; + + // |Display| + virtual double GetDevicePixelRatio() const override; + + private: + std::shared_ptr napi_facade_; + FML_DISALLOW_COPY_AND_ASSIGN(OHOSDisplay); +}; +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_DISPLAY_H_ \ No newline at end of file diff --git a/shell/platform/ohos/ohos_egl_surface.cpp b/shell/platform/ohos/ohos_egl_surface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2334f0ff99da4bdd53b76947c15f330644e0163a --- /dev/null +++ b/shell/platform/ohos/ohos_egl_surface.cpp @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/ohos_egl_surface.h" + +#include +#include + +#include "flutter/fml/trace_event.h" + +namespace flutter { + +void LogLastEGLError() { + struct EGLNameErrorPair { + const char* name; + EGLint code; + }; + + const EGLNameErrorPair pairs[] = { + {"EGL_SUCCESS", EGL_SUCCESS}, + {"EGL_NOT_INITIALIZED", EGL_NOT_INITIALIZED}, + {"EGL_BAD_ACCESS", EGL_BAD_ACCESS}, + {"EGL_BAD_ALLOC", EGL_BAD_ALLOC}, + {"EGL_BAD_ATTRIBUTE", EGL_BAD_ATTRIBUTE}, + {"EGL_BAD_CONTEXT", EGL_BAD_CONTEXT}, + {"EGL_BAD_CONFIG", EGL_BAD_CONFIG}, + {"EGL_BAD_CURRENT_SURFACE", EGL_BAD_CURRENT_SURFACE}, + {"EGL_BAD_DISPLAY", EGL_BAD_DISPLAY}, + {"EGL_BAD_SURFACE", EGL_BAD_SURFACE}, + {"EGL_BAD_MATCH", EGL_BAD_MATCH}, + {"EGL_BAD_PARAMETER", EGL_BAD_PARAMETER}, + {"EGL_BAD_NATIVE_PIXMAP", EGL_BAD_NATIVE_PIXMAP}, + {"EGL_BAD_NATIVE_WINDOW", EGL_BAD_NATIVE_WINDOW}, + {"EGL_CONTEXT_LOST", EGL_CONTEXT_LOST}}; + + const auto count = sizeof(pairs) / sizeof(EGLNameErrorPair); + + EGLint last_error = eglGetError(); + + for (size_t i = 0; i < count; i++) { + if (last_error == pairs[i].code) { + FML_LOG(ERROR) << "EGL Error: " << pairs[i].name << " (" << pairs[i].code + << ")"; + return; + } + } + + FML_LOG(ERROR) << "Unknown EGL Error"; +} + +namespace { + +static bool HasExtension(const char* extensions, const char* name) { + const char* r = strstr(extensions, name); + auto len = strlen(name); + // check that the extension name is terminated by space or null terminator + return r != nullptr && (r[len] == ' ' || r[len] == 0); +} + +} // namespace + +class OhosEGLSurfaceDamage { + public: + void init(EGLDisplay display, EGLContext context) { + const char* extensions = eglQueryString(display, EGL_EXTENSIONS); + if (HasExtension(extensions, "EGL_KHR_partial_update")) { + set_damage_region_ = reinterpret_cast( + eglGetProcAddress("eglSetDamageRegionKHR")); + } + if (HasExtension(extensions, "EGL_EXT_swap_buffers_with_damage")) { + swap_buffers_with_damage_ = + reinterpret_cast( + eglGetProcAddress("eglSwapBuffersWithDamageEXT")); + } else if (HasExtension(extensions, "EGL_KHR_swap_buffers_with_damage")) { + swap_buffers_with_damage_ = + reinterpret_cast( + eglGetProcAddress("eglSwapBuffersWithDamageKHR")); + } + + partial_redraw_supported_ = false; + } + + void SetDamageRegion(EGLDisplay display, + EGLSurface surface, + const std::optional& region) { + if (partial_redraw_supported_ && set_damage_region_ && region) { + auto rects = RectToInts(display, surface, *region); + set_damage_region_(display, surface, rects.data(), 1); + } + } + + // Maximum damage history - for triple buffering we need to store damage for + // last two frames; Some Ohos devices (Pixel 4) use quad buffering. + static const int kMaxHistorySize = 10; + + bool SupportsPartialRepaint() const { return partial_redraw_supported_; } + + std::optional InitialDamage(EGLDisplay display, EGLSurface surface) { + if (!partial_redraw_supported_) { + return std::nullopt; + } + + EGLint age; + eglQuerySurface(display, surface, EGL_BUFFER_AGE_EXT, &age); + + if (age == 0) { // full repaint + return std::nullopt; + } else { + // join up to (age - 1) last rects from damage history + --age; + auto res = SkIRect::MakeEmpty(); + for (auto i = damage_history_.rbegin(); + i != damage_history_.rend() && age > 0; ++i, --age) { + res.join(*i); + } + return res; + } + } + + bool SwapBuffersWithDamage(EGLDisplay display, + EGLSurface surface, + const std::optional& damage) { + if (partial_redraw_supported_ && swap_buffers_with_damage_ && damage) { + damage_history_.push_back(*damage); + if (damage_history_.size() > kMaxHistorySize) { + damage_history_.pop_front(); + } + auto rects = RectToInts(display, surface, *damage); + return swap_buffers_with_damage_(display, surface, rects.data(), 1); + } else { + return eglSwapBuffers(display, surface); + } + } + + private: + std::array static RectToInts(EGLDisplay display, + EGLSurface surface, + const SkIRect& rect) { + EGLint height; + eglQuerySurface(display, surface, EGL_HEIGHT, &height); + + std::array res{rect.left(), height - rect.bottom(), rect.width(), + rect.height()}; + return res; + } + + PFNEGLSETDAMAGEREGIONKHRPROC set_damage_region_ = nullptr; + PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage_ = nullptr; + + bool partial_redraw_supported_; + + std::list damage_history_; +}; + +OhosEGLSurface::OhosEGLSurface(EGLSurface surface, + EGLDisplay display, + EGLContext context) + : surface_(surface), + display_(display), + context_(context), + damage_(std::make_unique()), + presentation_time_proc_(nullptr) { + FML_LOG(INFO) << "OhosEGLSurface start"; + damage_->init(display_, context); + FML_LOG(INFO) << "OhosEGLSurface end"; +} + +OhosEGLSurface::~OhosEGLSurface() { + [[maybe_unused]] auto result = eglDestroySurface(display_, surface_); + FML_DCHECK(result == EGL_TRUE); +} + +bool OhosEGLSurface::IsValid() const { + return surface_ != EGL_NO_SURFACE; +} + +bool OhosEGLSurface::IsContextCurrent() const { + EGLContext current_egl_context = eglGetCurrentContext(); + if (context_ != current_egl_context) { + return false; + } + + EGLDisplay current_egl_display = eglGetCurrentDisplay(); + if (display_ != current_egl_display) { + return false; + } + + EGLSurface draw_surface = eglGetCurrentSurface(EGL_DRAW); + if (draw_surface != surface_) { + return false; + } + + EGLSurface read_surface = eglGetCurrentSurface(EGL_READ); + if (read_surface != surface_) { + return false; + } + + return true; +} + +OhosEGLSurfaceMakeCurrentStatus OhosEGLSurface::MakeCurrent() const { + if (IsContextCurrent()) { + return OhosEGLSurfaceMakeCurrentStatus::kSuccessAlreadyCurrent; + } + if (eglMakeCurrent(display_, surface_, surface_, context_) != EGL_TRUE) { + FML_LOG(ERROR) << "Could not make the context current"; + LogLastEGLError(); + return OhosEGLSurfaceMakeCurrentStatus::kFailure; + } + return OhosEGLSurfaceMakeCurrentStatus::kSuccessMadeCurrent; +} + +void OhosEGLSurface::SetDamageRegion( + const std::optional& buffer_damage) { + damage_->SetDamageRegion(display_, surface_, buffer_damage); +} + +bool OhosEGLSurface::SetPresentationTime( + const fml::TimePoint& presentation_time) { + if (presentation_time_proc_) { + const auto time_ns = presentation_time.ToEpochDelta().ToNanoseconds(); + return presentation_time_proc_(display_, surface_, time_ns); + } else { + return false; + } +} + +bool OhosEGLSurface::SwapBuffers(const std::optional& surface_damage) { + TRACE_EVENT0("flutter", "OhosContextGL::SwapBuffers"); + return damage_->SwapBuffersWithDamage(display_, surface_, surface_damage); +} + +bool OhosEGLSurface::SupportsPartialRepaint() const { + return damage_->SupportsPartialRepaint(); +} + +std::optional OhosEGLSurface::InitialDamage() { + return damage_->InitialDamage(display_, surface_); +} + +SkISize OhosEGLSurface::GetSize() const { + EGLint width = 0; + EGLint height = 0; + + if (!eglQuerySurface(display_, surface_, EGL_WIDTH, &width) || + !eglQuerySurface(display_, surface_, EGL_HEIGHT, &height)) { + FML_LOG(ERROR) << "Unable to query EGL surface size"; + LogLastEGLError(); + return SkISize::Make(0, 0); + } + return SkISize::Make(width, height); +} + +} // namespace flutter diff --git a/shell/platform/ohos/ohos_egl_surface.h b/shell/platform/ohos/ohos_egl_surface.h new file mode 100755 index 0000000000000000000000000000000000000000..49a74385275bd53ee029ab6d4adbb8dd058492df --- /dev/null +++ b/shell/platform/ohos/ohos_egl_surface.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_EGL_SURFACE_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_EGL_SURFACE_H_ + +#include +#define EGL_EGLEXT_PROTOTYPES +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/time/time_point.h" +#include "flutter/shell/platform/ohos/ohos_environment_gl.h" +#include "third_party/skia/include/core/SkRect.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// Holds an `EGLSurface` reference. +/// +/// +/// This can be used in conjunction to unique_ptr to provide better guarantees +/// about the lifespan of the `EGLSurface` object. +/// +class OhosEGLSurfaceDamage; + +/// Result of calling MakeCurrent on OhosEGLSurface. +enum class OhosEGLSurfaceMakeCurrentStatus { + /// Success, the egl context for the surface was already current. + kSuccessAlreadyCurrent, + /// Success, the egl context for the surface made current. + kSuccessMadeCurrent, + /// Failed to make the egl context for the surface current. + kFailure, +}; + +void LogLastEGLError(); + +class OhosEGLSurface { + public: + OhosEGLSurface(EGLSurface surface, EGLDisplay display, EGLContext context); + + ~OhosEGLSurface(); + + //---------------------------------------------------------------------------- + /// @return Whether the current `EGLSurface` reference is valid. That is, + /// if + /// the surface doesn't point to `EGL_NO_SURFACE`. + /// + bool IsValid() const; + + //---------------------------------------------------------------------------- + /// @brief Binds the EGLContext context to the current rendering thread + /// and to the draw and read surface. + /// + /// @return Whether the surface was made current. + /// + OhosEGLSurfaceMakeCurrentStatus MakeCurrent() const; + + //---------------------------------------------------------------------------- + /// + /// @return Whether target surface supports partial repaint. + /// + bool SupportsPartialRepaint() const; + + //---------------------------------------------------------------------------- + /// @brief This is the minimal area that needs to be repainted to get + /// correct result. + /// + /// With double or triple buffering this buffer content may lag behind + /// current front buffer and the rect accounts for accumulated damage. + /// + /// @return The area of current surface where it is behind front buffer. + /// + std::optional InitialDamage(); + + //---------------------------------------------------------------------------- + /// @brief Sets the damage region for current surface. Corresponds to + // eglSetDamageRegionKHR + void SetDamageRegion(const std::optional& buffer_damage); + + //---------------------------------------------------------------------------- + /// @brief Sets the presentation time for the current surface. This + // corresponds to calling eglPresentationTimeOhos when + // available. + bool SetPresentationTime(const fml::TimePoint& presentation_time); + + //---------------------------------------------------------------------------- + /// @brief This only applies to on-screen surfaces such as those created + /// by `OhosContextGL::CreateOnscreenSurface`. + /// + /// @return Whether the EGL surface color buffer was swapped. + /// + bool SwapBuffers(const std::optional& surface_damage); + + //---------------------------------------------------------------------------- + /// @return The size of an `EGLSurface`. + /// + SkISize GetSize() const; + + /// Returns true if the EGLContext held is current for the display and surface + bool IsContextCurrent() const; + + private: + const EGLSurface surface_; + const EGLDisplay display_; + const EGLContext context_; + std::unique_ptr damage_; + PFNEGLPRESENTATIONTIMEANDROIDPROC presentation_time_proc_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_EGL_SURFACE_H_ diff --git a/shell/platform/ohos/ohos_environment_gl.cpp b/shell/platform/ohos/ohos_environment_gl.cpp new file mode 100755 index 0000000000000000000000000000000000000000..d9ce63212c30bd9ca81928cf7fe193b327b5376c --- /dev/null +++ b/shell/platform/ohos/ohos_environment_gl.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/ohos_environment_gl.h" +#include "fml/trace_event.h" + +namespace flutter { + +OhosEnvironmentGL::OhosEnvironmentGL() + : display_(EGL_NO_DISPLAY), valid_(false) { + // Get the display. + TRACE_EVENT0("flutter", "OhosEnvironmentGL"); + display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + if (display_ == EGL_NO_DISPLAY) { + return; + } + + // Initialize the display connection. + if (eglInitialize(display_, nullptr, nullptr) != EGL_TRUE) { + return; + } + + valid_ = true; +} + +OhosEnvironmentGL::~OhosEnvironmentGL() { + // Disconnect the display if valid. + if (display_ != EGL_NO_DISPLAY) { + eglTerminate(display_); + } +} + +bool OhosEnvironmentGL::IsValid() const { + return valid_; +} + +EGLDisplay OhosEnvironmentGL::Display() const { + return display_; +} + +} // namespace flutter diff --git a/shell/platform/ohos/ohos_environment_gl.h b/shell/platform/ohos/ohos_environment_gl.h new file mode 100755 index 0000000000000000000000000000000000000000..ddd322075f2b4408e65ed9cc87b4f30df2353e85 --- /dev/null +++ b/shell/platform/ohos/ohos_environment_gl.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_ENVIRONMENT_GL_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_ENVIRONMENT_GL_H_ + +#include "flutter/fml/macros.h" +#include "flutter/fml/memory/ref_counted.h" + +#include + +namespace flutter { + +class OhosEnvironmentGL : public fml::RefCountedThreadSafe { + private: + // MakeRefCounted + OhosEnvironmentGL(); + + // MakeRefCounted + ~OhosEnvironmentGL(); + + public: + bool IsValid() const; + + EGLDisplay Display() const; + + private: + EGLDisplay display_; + bool valid_; + + FML_FRIEND_MAKE_REF_COUNTED(OhosEnvironmentGL); + FML_FRIEND_REF_COUNTED_THREAD_SAFE(OhosEnvironmentGL); + FML_DISALLOW_COPY_AND_ASSIGN(OhosEnvironmentGL); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_ENVIRONMENT_GL_H_ diff --git a/shell/platform/ohos/ohos_external_texture.cpp b/shell/platform/ohos/ohos_external_texture.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6671a91384a2a139662b28efd4ad5e42a3530a20 --- /dev/null +++ b/shell/platform/ohos/ohos_external_texture.cpp @@ -0,0 +1,1035 @@ +#include "ohos_external_texture.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fml/trace_event.h" +#include "include/core/SkM44.h" +#include "include/core/SkMatrix.h" +#include "ohos_main.h" + +namespace flutter { + +#define MAX_DELAYED_FRAMES 3 +#define MAX_SIZE_CHANGE_FRAMES 10 + +static int PixelMapToWindowFormat(PIXEL_FORMAT pixel_format) { + switch (pixel_format) { + case PIXEL_FORMAT_RGB_565: + return NATIVEBUFFER_PIXEL_FMT_RGB_565; + case PIXEL_FORMAT_RGBA_8888: + return NATIVEBUFFER_PIXEL_FMT_RGBA_8888; + case PIXEL_FORMAT_BGRA_8888: + return NATIVEBUFFER_PIXEL_FMT_BGRA_8888; + case PIXEL_FORMAT_RGB_888: + return NATIVEBUFFER_PIXEL_FMT_RGB_888; + case PIXEL_FORMAT_NV21: + return NATIVEBUFFER_PIXEL_FMT_YCRCB_420_SP; + case PIXEL_FORMAT_NV12: + return NATIVEBUFFER_PIXEL_FMT_YCBCR_420_SP; + case PIXEL_FORMAT_RGBA_1010102: + return NATIVEBUFFER_PIXEL_FMT_RGBA_1010102; + case PIXEL_FORMAT_YCBCR_P010: + return NATIVEBUFFER_PIXEL_FMT_YCBCR_P010; + case PIXEL_FORMAT_YCRCB_P010: + return NATIVEBUFFER_PIXEL_FMT_YCRCB_P010; + case PIXEL_FORMAT_ALPHA_8: + case PIXEL_FORMAT_RGBA_F16: + case PIXEL_FORMAT_UNKNOWN: + default: + // no support/unknow format: cannot copy + return 0; + } + return 0; +} + +static bool IsPixelMapYUVFormat(PIXEL_FORMAT format) { + return format == PIXEL_FORMAT_NV21 || format == PIXEL_FORMAT_NV12 || + format == PIXEL_FORMAT_YCBCR_P010 || format == PIXEL_FORMAT_YCRCB_P010; +} + +OHOSExternalTexture::OHOSExternalTexture(int64_t id, + OH_OnFrameAvailableListener listener) + : Texture(id), transform_(SkMatrix::I()), frame_listener_(listener) { + native_image_source_ = OH_NativeImage_Create(0, GL_TEXTURE_EXTERNAL_OES); + if (native_image_source_ == nullptr) { + FML_LOG(ERROR) << "Error with OH_NativeImage_Create"; + return; + } + + producer_nativewindow_ = + OH_NativeImage_AcquireNativeWindow(native_image_source_); + FML_LOG(INFO) << "OH_NativeImage_AcquireNativeWindow " + << producer_nativewindow_; + + if (!SetNativeWindowCPUAccess(producer_nativewindow_, false)) { + FML_LOG(ERROR) << "Error with SetNativeWindowCPUAccess"; + } + + int ret = OH_NativeImage_SetOnFrameAvailableListener(native_image_source_, + frame_listener_); + if (ret != 0) { + FML_LOG(ERROR) << "Error with OH_NativeImage_SetOnFrameAvailableListener " + << ret; + } + + is_emulator_ = OhosMain::IsEmulator(); +} + +OHOSExternalTexture::~OHOSExternalTexture() { + FML_LOG(INFO) << "~OHOSExternalTexture " << Id(); + DestroyNativeImageSource(); + DestroyPixelMapBuffer(); + return; +} + +void OHOSExternalTexture::Paint(PaintContext& context, + const SkRect& bounds, + bool freeze, + DlImageSampling sampling) { + SkRect new_bounds = bounds; + sk_sp draw_dl_image; + + if (bounds != old_draw_bounds_) { + draw_size_has_changed_ = true; + } else { + draw_size_has_changed_ = false; + } + + if (freeze || + (draw_dl_image = GetNextDrawImage(context, bounds)) == nullptr) { + draw_dl_image = GetOldDlImage(context, bounds); + } else { + SetOldDlImage(draw_dl_image); + } + + if (size_is_changing_ && draw_size_has_changed_ && + !buffer_size_has_changed_) { + // When the size is changing and the draw size changes first, rendering with + // the new size is not allowed to avoid stretched visuals—therefore, the + // previous draw size should be used for rendering. + new_bounds = old_draw_bounds_; + } else { + old_draw_bounds_ = bounds; + } + + if (size_is_changing_) { + size_change_frames_++; + if ((buffer_size_has_changed_ && draw_size_has_changed_) || + size_change_frames_ > MAX_SIZE_CHANGE_FRAMES) { + size_is_changing_ = false; + if (size_change_frames_ > MAX_SIZE_CHANGE_FRAMES) { + FML_LOG(INFO) << "stop size change state: frame > " + << MAX_SIZE_CHANGE_FRAMES; + } else { + FML_LOG(INFO) << "size change took " << size_change_frames_ + << " frames."; + } + size_change_frames_ = 0; + } + } + + if (draw_dl_image) { + DlAutoCanvasRestore auto_restore(context.canvas, true); + SkM44 new_transform; + GetNewTransformBound(new_transform, new_bounds); + context.canvas->Transform(new_transform); + context.canvas->DrawImageRect( + draw_dl_image, // image + SkRect::Make(draw_dl_image->bounds()), // source rect + new_bounds, // destination rect + sampling, // sampling + context.paint, // paint + flutter::DlCanvas::SrcRectConstraint::kStrict // enforce edges + ); + context.canvas->Flush(); + } else { + // ready for fix black background issue when external texture is not ready. + // note: it may be incorrect because the background color should be set in + // dart DlAutoCanvasRestore auto_restore(context.canvas, true); DlPaint + // paint; paint.setColor(DlColor::kWhite()); + // context.canvas->DrawRect(bounds, paint); + FML_LOG(INFO) << "No DlImage available for ImageExternalTexture to paint."; + } +} + +void OHOSExternalTexture::MarkNewFrameAvailable() { + if (now_new_frame_seq_num_ - now_paint_frame_seq_num_ > 5 || + now_paint_frame_seq_num_ % 60 == 0) { + FML_LOG(INFO) << " OHOSExternalTexture::MarkNewFrameAvailable avail-seq " + << now_new_frame_seq_num_ << " paint-seq " + << now_paint_frame_seq_num_ << " texture_id " << Id(); + } + now_new_frame_seq_num_++; + producer_has_frame_ = true; + if (producer_nativewindow_ != nullptr && native_image_source_ != nullptr) { + int buffer_queue_size = 0; + int ret = OH_NativeWindow_NativeWindowHandleOpt( + producer_nativewindow_, GET_BUFFERQUEUE_SIZE, &buffer_queue_size); + if (ret != 0 || buffer_queue_size > 100) { + FML_LOG(INFO) << " MarkNewFrameAvailable get error buffer queue size " + << buffer_queue_size << " ret " << ret; + return; + } + // Here we release the buffers in the buffer_queue to ensure there is always + // space in the queue, preventing the producer side from stalling. + int max_jank_frame = buffer_queue_size * 2 / 3; + while (max_jank_frame > 1 && + now_new_frame_seq_num_ - now_paint_frame_seq_num_ >= + max_jank_frame) { + OHNativeWindowBuffer* buffer = nullptr; + int fence_fd; + ret = OH_NativeImage_AcquireNativeWindowBuffer(native_image_source_, + &buffer, &fence_fd); + if (buffer != nullptr && ret == 0) { + FML_LOG(INFO) << "external_texture skip one frame(slow consumer): " + << buffer << " buffer_queue_size " << buffer_queue_size + << " max_jank_frame " << max_jank_frame; + ReleaseWindowBuffer(native_image_source_, buffer, &fence_fd); + now_paint_frame_seq_num_++; + buffer = nullptr; + } else { + FML_LOG(ERROR) << "MarkNewFrameAvailable AcquireBuffer error ret:" + << ret << " buffer_queue_size " << buffer_queue_size + << " max_jank_frame " << max_jank_frame + << " NativeImage " << native_image_source_; + now_paint_frame_seq_num_ = (int64_t)now_new_frame_seq_num_; + break; + } + } + } +} + +void OHOSExternalTexture::OnTextureUnregistered() { + FML_LOG(INFO) << " OHOSExternalTexture::OnTextureUnregistered"; + // GPU resource must be release here (in raster thread). + // Otherwise, gpu memory will leak. + old_dl_image_.reset(); + image_lru_.Clear(); + if (FdIsValid(last_fence_fd_)) { + close(last_fence_fd_); + } + last_fence_fd_ = -1; + GPUResourceDestroy(); +} + +void OHOSExternalTexture::OnGrContextCreated() { + FML_LOG(INFO) << "OnGrContextCreated texture_id " << Id(); + if (native_image_source_ == nullptr) { + return; + } + // move SetOnFrame here to avoid MarkNewFrameAvailable being invoked when + // rasterizer thread is in starting. Hit: MarkNewFrameAvailable will be + // invoked in rasterizer thread. + int ret = OH_NativeImage_SetOnFrameAvailableListener(native_image_source_, + frame_listener_); + FML_LOG(INFO) + << "OnGrContextCreated OH_NativeImage_SetOnFrameAvailableListener "; + if (ret != 0) { + FML_LOG(ERROR) << "Error with OH_NativeImage_SetOnFrameAvailableListener " + << ret; + } +} + +void OHOSExternalTexture::OnGrContextDestroyed() { + // move SetOnFrame using default listener here to avoid MarkNewFrameAvailable + // being invoked when rasterizer thread exit. Hit: MarkNewFrameAvailable will + // be invoked in rasterizer thread. + if (native_image_source_ == nullptr) { + return; + } + OH_OnFrameAvailableListener listener; + listener.context = (void*)native_image_source_; + listener.onFrameAvailable = &OHOSExternalTexture::DefaultOnFrameAvailable; + OH_NativeImage_SetOnFrameAvailableListener(native_image_source_, listener); + // when GrContextDestroyed invoking, we just need release gpu resource. + FML_LOG(INFO) << "OnGrContextDestroyed release gpu resource texture_id " + << Id(); + old_dl_image_.reset(); + image_lru_.Clear(); + if (FdIsValid(last_fence_fd_)) { + close(last_fence_fd_); + } + last_fence_fd_ = -1; + GPUResourceDestroy(); +} + +uint64_t OHOSExternalTexture::GetProducerSurfaceId() { + if (native_image_source_ == nullptr) { + return 0; + } + int ret = + OH_NativeImage_GetSurfaceId(native_image_source_, &producer_surface_id_); + if (ret != 0) { + FML_LOG(ERROR) << "Error with OH_NativeImage_GetSurfaceId " << ret; + return 0; + } + FML_LOG(INFO) << "OH_NativeImage_GetSurfaceId " << producer_surface_id_; + return producer_surface_id_; +} + +uint64_t OHOSExternalTexture::GetProducerWindowId() { + if (native_image_source_ == nullptr) { + return 0; + } + if (producer_nativewindow_ == nullptr) { + producer_nativewindow_ = + OH_NativeImage_AcquireNativeWindow(native_image_source_); + } + return (uint64_t)producer_nativewindow_; +} + +bool OHOSExternalTexture::SetPixelMapAsProducer( + NativePixelMap* pixelMap, + OH_NativeBuffer* pixelMap_native_buffer) { + TRACE_EVENT0("flutter", "SetPixelMapAsProducer"); + int32_t ret = -1; + OhosPixelMapInfos pixelmap_info; + if (pixelMap == nullptr) { + FML_LOG(ERROR) + << "OHOSExternalTextureGL SetPixelMapAsProducer get null pixelmap"; + return false; + } + ret = OH_PixelMap_GetImageInfo(pixelMap, &pixelmap_info); + if (ret != 0) { + FML_LOG(ERROR) << "OHOSExternalTextureGL OH_PixelMap_GetImageInfo err:" + << ret; + return false; + } + + // we needn't do copy when NativeBuffer is available. + if (pixelMap_native_buffer != nullptr) { + DestroyPixelMapBuffer(); + pixelmap_buffer_ = OH_NativeWindow_CreateNativeWindowBufferFromNativeBuffer( + pixelMap_native_buffer); + if (pixelmap_buffer_ != nullptr) { + pixelmap_native_buffer_ = pixelMap_native_buffer; + FML_LOG(INFO) + << "SetPixelMapAsProducer use direct native_buffer(without copy) " + << pixelmap_native_buffer_; + return true; + } + // when creating OHNativeWindowBuffer failed, we release the OH_NativeBuffer + // and go the copy path. + OH_NativeBuffer_Unreference(pixelMap_native_buffer); + } + + unsigned char* pixel_addr = nullptr; + ret = OH_PixelMap_AccessPixels(pixelMap, (void**)&pixel_addr); + if (ret != IMAGE_RESULT_SUCCESS || pixel_addr == nullptr) { + FML_LOG(ERROR) << "OHOSExternalTextureGL OH_PixelMap_AccessPixels err:" + << ret; + return false; + } + + std::string trace_str = "(copy image size-" + + std::to_string(pixelmap_info.width) + "*" + + std::to_string(pixelmap_info.height) + " format " + + std::to_string(pixelmap_info.pixelFormat) + ")"; + TRACE_EVENT1("flutter", "SetPixelMapAsProducer", "info", trace_str.c_str()); + + FML_LOG(INFO) << "SetPixelMapAsProducer with copy"; + bool end_ret = true; + if (!CreatePixelMapBuffer(pixelmap_info.width, pixelmap_info.height, + pixelmap_info.pixelFormat) || + !CopyDataToPixelMapBuffer(pixel_addr, pixelmap_info.width, + pixelmap_info.height, pixelmap_info.rowSize, + pixelmap_info.pixelFormat)) { + FML_LOG(ERROR) << "SetPixelMapAsProducer not ok"; + end_ret = false; + } + + ret = OH_PixelMap_UnAccessPixels(pixelMap); + if (ret != IMAGE_RESULT_SUCCESS) { + FML_LOG(FATAL) << "OHOSExternalTextureGL OH_PixelMap_UnAccessPixels err:" + << ret; + return false; + } + + return end_ret; +} + +void OHOSExternalTexture::ReleaseWindowBuffer(OH_NativeImage* native_image, + OHNativeWindowBuffer* buffer, + int* fence_fd) { + int temp_fence_fd = -1; + if (fence_fd == nullptr) { + fence_fd = &temp_fence_fd; + } + // OH_NativeImage_ReleaseNativeWindowBuffer will close the fence_fd even if it + // fails. + int ret = + OH_NativeImage_ReleaseNativeWindowBuffer(native_image, buffer, *fence_fd); + if (ret != 0) { + FML_LOG(ERROR) << "OHOSExternalTexture ReleaseNativeWindowBuffe(Get " + "Last) get err:" + << ret; + OH_NativeWindow_DestroyNativeWindowBuffer(buffer); + } + *fence_fd = -1; + return; +} + +SkRect OHOSExternalTexture::UpdateWindowSize(OHNativeWindowBuffer* buffer) { + OH_NativeBuffer_Config config = {0, 0}; + OH_NativeBuffer* native_buffer = nullptr; + int ret = OH_NativeBuffer_FromNativeWindowBuffer(buffer, &native_buffer); + if (native_buffer != nullptr) { + OH_NativeBuffer_GetConfig(native_buffer, &config); + producer_nativewindow_width_ = config.width; + producer_nativewindow_height_ = config.height; + } + return {0, 0, static_cast(producer_nativewindow_width_), + static_cast(producer_nativewindow_height_)}; +} + +OHNativeWindowBuffer* OHOSExternalTexture::GetConsumerNativeBuffer( + int* fence_fd) { + if (!producer_has_frame_) { + return pixelmap_buffer_; + } else { + if (pixelmap_buffer_ != nullptr) { + DestroyPixelMapBuffer(); + } + } + if (native_image_source_ == nullptr) { + return nullptr; + } + OHNativeWindowBuffer* now_nw_buffer = nullptr; + int ret = OH_NativeImage_AcquireNativeWindowBuffer(native_image_source_, + &now_nw_buffer, fence_fd); + if ((now_nw_buffer == nullptr && size_change_buffer_ == nullptr) || + ret != 0) { + // buffer_queue is empty. + now_paint_frame_seq_num_ = (int64_t)now_new_frame_seq_num_; + return nullptr; + } + if (*fence_fd <= 0 && now_paint_frame_seq_num_ % 60 == 0) { + FML_DLOG(INFO) << "get not null native_window_buffer but inValid fence_fd: " + << *fence_fd; + } + + if (now_nw_buffer != nullptr) { + if (size_change_buffer_ != nullptr) { + // release old size_change_buffer + ReleaseWindowBuffer(native_image_source_, size_change_buffer_, + &size_change_buffer_fence_fd_); + now_paint_frame_seq_num_++; + } + } else { + // reuse old size_change_buffer + now_nw_buffer = size_change_buffer_; + *fence_fd = size_change_buffer_fence_fd_; + } + size_change_buffer_ = nullptr; + size_change_buffer_fence_fd_ = -1; + + SkRect now_buffer_bounds = UpdateWindowSize(now_nw_buffer); + if (now_buffer_bounds != old_buffer_bounds_) { + buffer_size_has_changed_ = true; + } else { + buffer_size_has_changed_ = false; + } + + if (size_is_changing_ && !draw_size_has_changed_ && + buffer_size_has_changed_) { + // When the size is changing and the buffer size changes first, the buffer + // cannot be used for rendering to avoid stretched visuals—thus, the buffer + // should be discarded, and the previous buffer should be used. + size_change_buffer_ = now_nw_buffer; + size_change_buffer_fence_fd_ = *fence_fd; + *fence_fd = -1; + FML_LOG(INFO) << "direct release size changed buffer because draw-size is " + "not changed."; + return nullptr; + } else { + old_buffer_bounds_ = now_buffer_bounds; + } + + if (last_native_window_buffer_ != nullptr) { + // Pixelmap does not require a fence to ensure synchronization. + if (pixelmap_buffer_ == nullptr) { + // Calling SetGPUFence here can reduce overhead while ensuring the correct + // placement of the fence in Vulkan mode. + if (FdIsValid(last_fence_fd_)) { + close(last_fence_fd_); + } + last_fence_fd_ = -1; + SetGPUFence(last_native_window_buffer_, &last_fence_fd_); + } + + ReleaseWindowBuffer(native_image_source_, last_native_window_buffer_, + &last_fence_fd_); + } + last_native_window_buffer_ = now_nw_buffer; + last_fence_fd_ = *fence_fd; + now_paint_frame_seq_num_++; + while (now_paint_frame_seq_num_ + MAX_DELAYED_FRAMES < + now_new_frame_seq_num_) { + OHNativeWindowBuffer* nw_buffer = nullptr; + int ret = OH_NativeImage_AcquireNativeWindowBuffer(native_image_source_, + &nw_buffer, fence_fd); + if (nw_buffer != nullptr && ret == 0) { + FML_LOG(INFO) << "external_texture skip one frame: " + << last_native_window_buffer_ << " fence_fd " + << last_fence_fd_; + ReleaseWindowBuffer(native_image_source_, last_native_window_buffer_, + &last_fence_fd_); + last_native_window_buffer_ = nw_buffer; + last_fence_fd_ = *fence_fd; + now_nw_buffer = nw_buffer; + now_paint_frame_seq_num_++; + } else { + now_paint_frame_seq_num_ = (int64_t)now_new_frame_seq_num_; + break; + } + } + + if (now_paint_frame_seq_num_ < now_new_frame_seq_num_ && + frame_listener_.onFrameAvailable != nullptr) { + // Reschedule new frame (notify new texture in the next frame) + now_new_frame_seq_num_--; + frame_listener_.onFrameAvailable(frame_listener_.context); + } + // Note that *fence_fd has same fd and will be close in WaitGPUFence, so we + // let last_fence_fd_ be -1. + last_fence_fd_ = -1; + return now_nw_buffer; +} + +sk_sp OHOSExternalTexture::GetNextDrawImage( + PaintContext& context, + const SkRect& bounds) { + int fence_fd = -1; + OHNativeWindowBuffer* native_widnow_buffer = + GetConsumerNativeBuffer(&fence_fd); + if (native_widnow_buffer == nullptr) { + return nullptr; + } + + OH_NativeBuffer* native_buffer = nullptr; + int ret = OH_NativeBuffer_FromNativeWindowBuffer(native_widnow_buffer, + &native_buffer); + if (ret != 0 || native_buffer == nullptr) { + FML_LOG(ERROR) << "OHOSExternalTextureGL get OH_NativeBuffer error:" << ret; + return nullptr; + } + // ensure buffer_id > 0 (may get seqNum = 0) + uint32_t buffer_id = OH_NativeBuffer_GetSeqNum(native_buffer) + 1; + + auto ret_image = image_lru_.FindImage(buffer_id); + if (ret_image == nullptr) { + ret_image = CreateDlImage(context, bounds, buffer_id, native_widnow_buffer); + } + if (ret_image == nullptr) { + if (FdIsValid(fence_fd)) { + close(fence_fd); + } + fence_fd = -1; + } else { + // let gpu wait for the nativebuffer end use + // fence_fd will be close in WaitGPUFence. + WaitGPUFence(fence_fd); + } + return ret_image; +} + +sk_sp OHOSExternalTexture::GetOldDlImage( + PaintContext& context, + const SkRect& bounds) { + if (!old_dl_image_ && last_native_window_buffer_ != nullptr) { + OH_NativeBuffer* native_buffer = nullptr; + int ret = OH_NativeBuffer_FromNativeWindowBuffer(last_native_window_buffer_, + &native_buffer); + if (ret != 0 || native_buffer == nullptr) { + FML_LOG(ERROR) << "OHOSExternalTextureGL get OH_NativeBuffer error:" + << ret; + return nullptr; + } + // ensure buffer_id > 0 (may get seqNum = 0) + uint32_t buffer_id = OH_NativeBuffer_GetSeqNum(native_buffer) + 1; + old_dl_image_ = + CreateDlImage(context, bounds, buffer_id, last_native_window_buffer_); + } + return old_dl_image_; +} + +void OHOSExternalTexture::SetOldDlImage(sk_sp old_image) { + old_dl_image_ = std::move(old_image); +} + +bool OHOSExternalTexture::SetProducerWindowSize(int width, int height) { + if (native_image_source_ == nullptr) { + return false; + } + if (producer_nativewindow_ == nullptr) { + producer_nativewindow_ = + OH_NativeImage_AcquireNativeWindow(native_image_source_); + if (producer_nativewindow_ == nullptr) { + FML_LOG(ERROR) + << "OHOSExternalTexture OH_NativeImage_AcquireNativeWindow get null"; + return false; + } + } + bool ret = SetWindowSize(producer_nativewindow_, width, height); + if (ret) { + producer_nativewindow_width_ = width; + producer_nativewindow_height_ = height; + } + return ret; +} + +void OHOSExternalTexture::NotifyResizing(int width, int height) { + if (width != producer_nativewindow_width_ || + height != producer_nativewindow_height_) { + size_is_changing_ = true; + } +} + +bool OHOSExternalTexture::SetExternalNativeImage(OH_NativeImage* native_image) { + if (native_image == nullptr) { + return false; + } + if (native_image == native_image_source_) { + FML_LOG(ERROR) << "SetExternalNativeImage set same image " << native_image; + return true; + } + int ret = + OH_NativeImage_SetOnFrameAvailableListener(native_image, frame_listener_); + if (ret != 0) { + FML_LOG(ERROR) << "ExternalNativeImage SetOnFrameAvailableListener failed:" + << ret; + return false; + } + // Clean all buffers in the bufferqueue to get correct frame_seq_num. + OHNativeWindowBuffer* buffer = nullptr; + int fence_fd = -1; + int release_cnt = 0; + ret = OH_NativeImage_AcquireNativeWindowBuffer(native_image, &buffer, + &fence_fd); + while (ret == 0 && buffer != nullptr) { + ReleaseWindowBuffer(native_image, buffer, &fence_fd); + release_cnt++; + buffer = nullptr; + fence_fd = -1; + ret = OH_NativeImage_AcquireNativeWindowBuffer(native_image, &buffer, + &fence_fd); + } + + FML_LOG(INFO) << "release external NativeImage " << release_cnt << " buffer"; + DestroyNativeImageSource(); + native_image_source_ = native_image; + producer_nativewindow_ = + OH_NativeImage_AcquireNativeWindow(native_image_source_); + source_is_external_ = true; + now_paint_frame_seq_num_ = 0; + now_new_frame_seq_num_ = 0; + + return true; +} + +uint64_t OHOSExternalTexture::Reset(bool need_surfaceId) { + FML_LOG(INFO) << "ResetExternalTexture need_surfaceId" << need_surfaceId; + + OnTextureUnregistered(); + DestroyNativeImageSource(); + if (need_surfaceId) { + native_image_source_ = OH_NativeImage_Create(0, GL_TEXTURE_EXTERNAL_OES); + if (native_image_source_ == nullptr) { + FML_LOG(ERROR) << "Error with OH_NativeImage_Create"; + return 0; + } + + producer_nativewindow_ = + OH_NativeImage_AcquireNativeWindow(native_image_source_); + if (producer_nativewindow_ == nullptr) { + FML_LOG(INFO) << "OH_NativeImage_AcquireNativeWindow failed"; + OH_NativeImage_Destroy(&native_image_source_); + native_image_source_ = nullptr; + return 0; + } + + int ret = OH_NativeImage_SetOnFrameAvailableListener(native_image_source_, + frame_listener_); + if (ret != 0) { + OH_NativeImage_Destroy(&native_image_source_); + native_image_source_ = nullptr; + FML_LOG(ERROR) << "Error with OH_NativeImage_SetOnFrameAvailableListener " + << ret; + return 0; + } + uint64_t surface_id = 0; + OH_NativeImage_GetSurfaceId(native_image_source_, &surface_id); + return surface_id; + } + return 0; +} + +bool OHOSExternalTexture::CreatePixelMapBuffer(int width, + int height, + int pixel_format) { + int fence_fd = -1; + DestroyPixelMapBuffer(); + + int window_format = PixelMapToWindowFormat((PIXEL_FORMAT)pixel_format); + + if (width == 0 || height == 0 || window_format == 0) { + return false; + } + + OH_NativeBuffer_Config config = {width, height, window_format, + NATIVEBUFFER_USAGE_HW_TEXTURE | + NATIVEBUFFER_USAGE_MEM_DMA | + NATIVEBUFFER_USAGE_CPU_WRITE, + 0x8}; + + OH_NativeBuffer* native_buffer = OH_NativeBuffer_Alloc(&config); + + if (native_buffer == nullptr) { + return false; + } + pixelmap_buffer_ = + OH_NativeWindow_CreateNativeWindowBufferFromNativeBuffer(native_buffer); + if (pixelmap_buffer_ == nullptr) { + OH_NativeBuffer_Unreference(native_buffer); + return false; + } + pixelmap_native_buffer_ = native_buffer; + return true; +} + +void OHOSExternalTexture::DestroyPixelMapBuffer() { + if (pixelmap_buffer_ != nullptr) { + OH_NativeWindow_DestroyNativeWindowBuffer(pixelmap_buffer_); + } + if (pixelmap_native_buffer_ != nullptr) { + OH_NativeBuffer_Unreference(pixelmap_native_buffer_); + FML_LOG(INFO) << "try DestroyPixelMapBuffer " << pixelmap_native_buffer_; + } + pixelmap_buffer_ = nullptr; + pixelmap_native_buffer_ = nullptr; +} + +void OHOSExternalTexture::DestroyNativeImageSource() { + if (native_image_source_) { + if (last_native_window_buffer_ != nullptr) { + ReleaseWindowBuffer(native_image_source_, last_native_window_buffer_, + &last_fence_fd_); + last_native_window_buffer_ = nullptr; + last_fence_fd_ = -1; + } + if (size_change_buffer_ != nullptr) { + ReleaseWindowBuffer(native_image_source_, size_change_buffer_, + &size_change_buffer_fence_fd_); + size_change_buffer_ = nullptr; + size_change_buffer_fence_fd_ = -1; + } + FML_LOG(INFO) << "OH_NativeImage_Destroy " << native_image_source_; + + if (!source_is_external_) { + // producer_nativewindow_ will be destroy and + // UnsetOnFrameAvailableListener will be invoked in + // OH_NativeImage_Destroy. + OH_NativeImage_Destroy(&native_image_source_); + native_image_source_ = nullptr; + } else { + // When native_image_source_ is set via SetExternalNativeImage, we do not + // destroy it. + // Instead, we set the default frame available callback to prevent the + // producer from stalling. + OH_OnFrameAvailableListener listener; + listener.context = (void*)native_image_source_; + listener.onFrameAvailable = &OHOSExternalTexture::DefaultOnFrameAvailable; + OH_NativeImage_SetOnFrameAvailableListener(native_image_source_, + listener); + native_image_source_ = nullptr; + } + producer_nativewindow_ = nullptr; + } + now_paint_frame_seq_num_ = 0; + now_new_frame_seq_num_ = 0; +} + +void OHOSExternalTexture::DefaultOnFrameAvailable(void* native_image_ptr) { + OH_NativeImage* native_image = (OH_NativeImage*)native_image_ptr; + OHNativeWindowBuffer* buffer = nullptr; + int fence_fd = -1; + int ret = OH_NativeImage_AcquireNativeWindowBuffer(native_image, &buffer, + &fence_fd); + if (buffer != nullptr && ret == 0) { + FML_LOG(INFO) << "direct release one frame: no consumer " << buffer; + ReleaseWindowBuffer(native_image, buffer, &fence_fd); + } +} + +bool OHOSExternalTexture::SetWindowSize(OHNativeWindow* window, + int width, + int height) { + if (window == nullptr) { + return false; + } + int ret = OH_NativeWindow_NativeWindowHandleOpt(window, SET_BUFFER_GEOMETRY, + width, height); + if (ret != 0) { + FML_LOG(ERROR) << "SetWindowSize get err:" << ret << " window:" << window; + return false; + } + return true; +} + +bool OHOSExternalTexture::SetWindowFormat(OHNativeWindow* window, int format) { + if (window == nullptr) { + return false; + } + int ret = OH_NativeWindow_NativeWindowHandleOpt(window, SET_FORMAT, format); + if (ret != 0) { + int old_format; + OH_NativeWindow_NativeWindowHandleOpt(window, GET_FORMAT, &old_format); + FML_LOG(ERROR) << "window set format failed! ret:" << ret + << " old_format:" << old_format << " set_format:" << format; + return false; + } + ret = OH_NativeWindow_NativeWindowHandleOpt(window, SET_STRIDE, 0x8); + if (ret != 0) { + FML_LOG(ERROR) << "NativeWindow set Stride failed:" << ret; + return false; + } + return true; +} + +bool OHOSExternalTexture::CPUWaitFence(int fence_fd, uint32_t timeout) { + if (fence_fd <= 0) { + return false; + } + struct pollfd poll_fd = {0}; + poll_fd.fd = fence_fd; + poll_fd.events = POLLIN; + + int ret = -1; + do { + ret = poll(&poll_fd, 1, timeout); + } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); + + if (ret == 0) { + ret = -1; + errno = ETIME; + } else if (ret > 0) { + ret = 0; + if (poll_fd.revents & (POLLERR | POLLNVAL)) { + ret = -1; + errno = EINVAL; + } + } + return ret < 0 ? -errno : 0; +} + +bool OHOSExternalTexture::CopyDataToPixelMapBuffer(const unsigned char* src, + int width, + int height, + int stride, + int pixelmap_format) { + if (src == nullptr || producer_nativewindow_ == nullptr || + pixelmap_buffer_ == nullptr) { + return false; + } + OH_NativeBuffer_Config nativebuffer_config; + + // native_buffer ptr is convert from nativeWindowBuffer inner member, so it + // don't need release + OH_NativeBuffer* native_buffer = nullptr; + int ret = + OH_NativeBuffer_FromNativeWindowBuffer(pixelmap_buffer_, &native_buffer); + if (ret != 0 || native_buffer == nullptr) { + FML_LOG(ERROR) << "OHOSExternalTextureGL get OH_NativeBuffer error:" << ret; + return false; + } + OH_NativeBuffer_GetConfig(native_buffer, &nativebuffer_config); + if (nativebuffer_config.width != width || + nativebuffer_config.height != height) { + FML_LOG(ERROR) << "OHOSExternalTextureGL " + "CopyDataToPixelMapBuffer size error: width " + << width << "->" << nativebuffer_config.width << " height " + << nativebuffer_config.height << "->" << height; + return false; + } + + unsigned char* dst = nullptr; + ret = OH_NativeBuffer_Map(native_buffer, (void**)&dst); + if (ret != 0 || dst == nullptr) { + FML_LOG(ERROR) << "OHOSExternalTextureGL " + "OH_NativeBuffer_Map err:" + << ret; + return false; + } + int real_height = height; + if (IsPixelMapYUVFormat((PIXEL_FORMAT)pixelmap_format)) { + // y is height, uv is height/2 + real_height = height + (height + 1) / 2; + } + for (int i = 0; i < real_height; i++) { + memcpy(dst + i * nativebuffer_config.stride, src + i * stride, + std::min(nativebuffer_config.stride, stride)); + } + + ret = OH_NativeBuffer_Unmap(native_buffer); + if (ret != 0 || dst == nullptr) { + FML_LOG(ERROR) << "OHOSExternalTextureGL " + "OH_NativeBuffer_Unmap err:" + << ret; + return false; + } + return true; +} + +bool OHOSExternalTexture::SetNativeWindowCPUAccess(OHNativeWindow* window, + bool cpuAccess) { + if (window == nullptr) { + return false; + } + uint64_t usage = 0; + int ret = OH_NativeWindow_NativeWindowHandleOpt(window, GET_USAGE, &usage); + if (ret != 0) { + FML_LOG(ERROR) << "OHOSExternalTexture " + "get window usage err:" + << ret << " window:" << window; + return false; + } + usage |= NATIVEBUFFER_USAGE_HW_TEXTURE; + if (cpuAccess) { + usage |= NATIVEBUFFER_USAGE_CPU_READ; + usage |= NATIVEBUFFER_USAGE_CPU_WRITE; + } else { + usage &= (~NATIVEBUFFER_USAGE_CPU_READ); + usage &= (~NATIVEBUFFER_USAGE_CPU_WRITE); + } + ret = OH_NativeWindow_NativeWindowHandleOpt(window, SET_USAGE, usage); + if (ret != 0) { + FML_LOG(ERROR) << "OHOSExternalTexture " + "set window usage err:" + << ret << " window:" << window; + return false; + } + return true; +} + +void OHOSExternalTexture::GetNewTransformBound(SkM44& transform, + SkRect& bounds) { + if (pixelmap_buffer_ != nullptr || native_image_source_ == nullptr) { + transform.setIdentity(); + if (is_emulator_) { + // do a flip-V if we are in emulator. + transform = transform.preConcat( + SkM44(1, 0, 0, 0, 0, -1, 0, bounds.height(), 0, 0, 1, 0, 0, 0, 0, 1)); + } + return; + } + + // TransformMatrixV2 performs a vertical flip by default. + // This occurs because it uses the left-bottom corner as (0,0) + // and the transformation follows the original texture order. + // However, the canvas' (0,0) are located at the + // left-top corner. Therefore, we need to flip it back to correct the + // orientation. + float matrix[16]; + OH_NativeImage_GetTransformMatrixV2(native_image_source_, matrix); + // for (int i = 0; i < 4; i++) { + // FML_LOG(INFO) << matrix[i*4+0] << " " << matrix[i*4+1] + // << " " << matrix[i*4+2] << " " + // << matrix[i*4+3]; + // } + + SkM44 transform_origin = SkM44::ColMajor(matrix); + + if (is_emulator_) { + // do a flip-V if we are in emulator. + transform_origin = transform_origin.preConcat( + SkM44(1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1)); + } + + // Note that SkM44's constructor parameters are in row-major order. + // This operate is to do a flip-V and translate it to origin + // place. + SkM44 transform_end = transform_origin.preConcat( + SkM44(1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1)); + if (transform_end.rc(0, 0) == 0 && transform_end.rc(1, 1) == 0) { + // it has flip 90/270 and has no flip-v/flip-h -> rotate 180 degree + int dx = transform_end.rc(0, 3); + int dy = transform_end.rc(1, 3); + if ((dx ^ dy) == 1) { + transform_end = transform_end.preConcat( + SkM44(-1, 0, 0, 1, 0, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1)); + } + } + + // The transformation matrix is used to transform texture coordinates. + // The range of texture coordinates is (0,1), so translation transformations + // are either 0 or 1. Now, we are transforming vertex coordinates, so the + // translation range must be adjusted to the width and height of the vertex + // range. + transform_end.setRC(0, 3, transform_end.rc(0, 3) * bounds.width()); + transform_end.setRC(1, 3, transform_end.rc(1, 3) * bounds.height()); + // for (int i = 0; i < 4; i++) { + // FML_LOG(INFO) << transform_end.rc(0, i) << " " << transform_end.rc(1, i) + // << " " << transform_end.rc(2, i) << " " + // << transform_end.rc(3, i); + // } + + // If a 90-degree rotation is applied, the width and height of the vertex + // range need to be swapped. + if (matrix[0] == 0 && matrix[5] == 0) { + bounds.setWH(bounds.height(), bounds.width()); + } + + transform = transform_end; + return; +} + +bool OHOSExternalTexture::FenceIsSignal(int fence_fd) { + if (fence_fd <= 0) { + return false; + } + struct pollfd poll_fd = {0}; + poll_fd.fd = fence_fd; + poll_fd.events = POLLIN; + + int ret = poll(&poll_fd, 1, 0); + return (ret > 0) && !(poll_fd.revents & (POLLERR | POLLNVAL)); +} + +bool OHOSExternalTexture::FdIsValid(int fd) { + if (fd <= 0) { + return false; + } + errno = 0; + struct stat file_stat = {}; + int ret = 0; + do { + ret = fstat(fd, &file_stat); + } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); + + if (ret == -1) { + if (errno != EBADF) { + FML_LOG(WARNING) << "check fd " << fd << " is valid, error:" << errno; + } + return false; + } else { + // anon_inode:sync_file is a chr device + if (S_ISCHR(file_stat.st_mode)) { + return true; + } else { + FML_LOG(WARNING) << "get no-sync_file fd " << fd + << " mode: " << file_stat.st_mode; + return false; + } + } +} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/ohos_external_texture.h b/shell/platform/ohos/ohos_external_texture.h new file mode 100644 index 0000000000000000000000000000000000000000..1d0f4bffc1f829ff9fe58f4147401accf7a39ab1 --- /dev/null +++ b/shell/platform/ohos/ohos_external_texture.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_EXTERNAL_TEXTURE_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_EXTERNAL_TEXTURE_H_ + +#include +#include +#include +#include +#include +#include + +#include "flutter/common/graphics/texture.h" + +#include "image_lru.h" +#include "include/core/SkRect.h" + +namespace flutter { + +class OHOSExternalTexture : public flutter::Texture { + public: + explicit OHOSExternalTexture(int64_t id, + OH_OnFrameAvailableListener listener); + + ~OHOSExternalTexture() override; + + void Paint(PaintContext& context, + const SkRect& bounds, + bool freeze, + DlImageSampling sampling) override; + + void OnGrContextCreated() override; + + void OnGrContextDestroyed() override; + + void MarkNewFrameAvailable() override; + + void OnTextureUnregistered() override; + + uint64_t GetProducerSurfaceId(); + + uint64_t GetProducerWindowId(); + + bool SetPixelMapAsProducer(NativePixelMap* pixelMap, + OH_NativeBuffer* pixelMap_native_buffer); + + bool SetProducerWindowSize(int width, int height); + + void NotifyResizing(int width, int height); + + // Replace the original native_image_source_ with the external native_image. + // This must be called on the raster thread to avoid concurrent operations + // on native_image_source_, as the OnFrameAvailable callback for native_image + // may be triggered immediately (the callback relies on native_image_source_ + // within the raster thread). + bool SetExternalNativeImage(OH_NativeImage* native_image); + + uint64_t Reset(bool need_surfaceId); + + static void DefaultOnFrameAvailable(void* native_image_ptr); + + static void ReleaseWindowBuffer(OH_NativeImage* native_image, + OHNativeWindowBuffer* buffer, + int* fence_fd); + + static bool FenceIsSignal(int fence_fd); + + // Check if the fd is a valid sync file. + static bool FdIsValid(int fd); + + protected: + OHNativeWindowBuffer* GetConsumerNativeBuffer(int* fence_fd); + + virtual void SetGPUFence(OHNativeWindowBuffer* window_buffer, + int* fence_fd) = 0; + virtual void WaitGPUFence(int fence_fd) { close(fence_fd); } + virtual void GPUResourceDestroy() = 0; + + virtual sk_sp CreateDlImage( + PaintContext& context, + const SkRect& bounds, + NativeBufferKey key, + OHNativeWindowBuffer* nw_buffer) = 0; + + ImageLRU image_lru_ = ImageLRU(); + + private: + sk_sp GetNextDrawImage(PaintContext& context, + const SkRect& bounds); + + sk_sp GetOldDlImage(PaintContext& context, + const SkRect& bounds); + + void SetOldDlImage(sk_sp old_image); + + bool CopyDataToPixelMapBuffer(const unsigned char* src, + int width, + int height, + int stride, + int pixelmap_format); + + bool CreatePixelMapBuffer(int width, int height, int pixel_format); + + void DestroyNativeImageSource(); + + void DestroyPixelMapBuffer(); + + SkRect UpdateWindowSize(OHNativeWindowBuffer* buffer); + + bool SetWindowSize(OHNativeWindow* window, int width, int height); + + bool SetWindowFormat(OHNativeWindow* window, int format); + + bool CPUWaitFence(int fence_fd, uint32_t timeout); + + bool SetNativeWindowCPUAccess(OHNativeWindow* window, bool cpuAccess); + + void GetNewTransformBound(SkM44& transform, SkRect& bounds); + + uint64_t producer_surface_id_ = 0; + + bool producer_has_frame_ = false; + int producer_nativewindow_width_ = 0; + int producer_nativewindow_height_ = 0; + OHNativeWindow* producer_nativewindow_ = nullptr; + OHNativeWindowBuffer* pixelmap_buffer_ = nullptr; + OH_NativeBuffer* pixelmap_native_buffer_ = nullptr; + + OHNativeWindowBuffer* last_native_window_buffer_ = nullptr; + int last_fence_fd_ = -1; + + std::atomic now_paint_frame_seq_num_ = 0; + + std::atomic now_new_frame_seq_num_ = 0; + + bool source_is_external_ = false; + OH_NativeImage* native_image_source_ = nullptr; + + SkMatrix transform_; + + sk_sp old_dl_image_; + SkRect old_buffer_bounds_ = {}; + SkRect old_draw_bounds_ = {}; + int size_change_frames_ = 0; + std::atomic size_is_changing_ = false; + bool draw_size_has_changed_ = true; + bool buffer_size_has_changed_ = true; + + OHNativeWindowBuffer* size_change_buffer_ = nullptr; + int size_change_buffer_fence_fd_ = -1; + + OH_OnFrameAvailableListener frame_listener_; + + bool is_emulator_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(OHOSExternalTexture); +}; +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_EXTERNAL_TEXTURE_H_ \ No newline at end of file diff --git a/shell/platform/ohos/ohos_external_texture_gl.cpp b/shell/platform/ohos/ohos_external_texture_gl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c5fb340ec24ed2580259f2b3c2ee82df415b9cb --- /dev/null +++ b/shell/platform/ohos/ohos_external_texture_gl.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "ohos_external_texture_gl.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "impeller/toolkit/egl/image.h" +#include "third_party/skia/include/core/SkAlphaType.h" +#include "third_party/skia/include/core/SkColorSpace.h" +#include "third_party/skia/include/core/SkColorType.h" +#include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/gpu/GrBackendSurface.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" +#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h" +#include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h" + +namespace flutter { + +PFNEGLCREATESYNCKHRPROC OHOSExternalTextureGL::eglCreateSyncKHR_ = nullptr; +PFNEGLDUPNATIVEFENCEFDANDROIDPROC +OHOSExternalTextureGL::eglDupNativeFenceFDANDROID_ = nullptr; +PFNEGLDESTROYSYNCKHRPROC OHOSExternalTextureGL::eglDestroySyncKHR_ = nullptr; +PFNEGLWAITSYNCKHRPROC OHOSExternalTextureGL::eglWaitSyncKHR_ = nullptr; +PFNEGLCREATEIMAGEKHRPROC OHOSExternalTextureGL::eglCreateImageKHR_ = nullptr; +PFNGLEGLIMAGETARGETTEXTURE2DOESPROC +OHOSExternalTextureGL::glEGLImageTargetTexture2DOES_ = nullptr; +PFNEGLDESTROYIMAGEKHRPROC OHOSExternalTextureGL::eglDestroyImageKHR_ = nullptr; + +OHOSExternalTextureGL::OHOSExternalTextureGL( + int64_t id, + OH_OnFrameAvailableListener listener) + : OHOSExternalTexture(id, listener) { + InitEGLFunPtr(); +} + +OHOSExternalTextureGL::~OHOSExternalTextureGL() {} + +void OHOSExternalTextureGL::SetGPUFence(OHNativeWindowBuffer* window_buffer, + int* fence_fd) { + EGLDisplay disp = eglGetCurrentDisplay(); + if (disp == EGL_NO_DISPLAY) { + return; + } + + OH_NativeBuffer* native_buffer = nullptr; + int ret = + OH_NativeBuffer_FromNativeWindowBuffer(window_buffer, &native_buffer); + if (ret != 0 || native_buffer == nullptr) { + FML_LOG(ERROR) << "OHOSExternalTextureGL get OH_NativeBuffer error:" << ret; + return; + } + + // ensure buffer_id > 0 (may get seqNum = 0) + uint32_t buffer_id = OH_NativeBuffer_GetSeqNum(native_buffer) + 1; + + if (eglCreateSyncKHR_ != nullptr && eglDupNativeFenceFDANDROID_ != nullptr && + eglDestroySyncKHR_ != nullptr) { + EGLSyncKHR fence_sync = + eglCreateSyncKHR_(disp, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + *fence_fd = eglDupNativeFenceFDANDROID_(disp, fence_sync); + FML_DLOG(INFO) << "create norma fence sync fd " << *fence_fd + << " fence_sync " << fence_sync << " eglError " + << eglGetError(); + glFlush(); + gl_resources_[buffer_id].wait_sync = UniqueEGLSync(fence_sync); + } else { + FML_LOG(ERROR) << "get null proc ptr eglCreateSyncKHR:" << eglCreateSyncKHR_ + << " eglDupNativeFenceFDANDROID:" + << eglDupNativeFenceFDANDROID_ + << " eglDestroySyncKHR:" << eglDestroySyncKHR_; + } + + EGLenum err = eglGetError(); + // 12288 is EGL_SUCCESS + if (err != EGL_SUCCESS) { + FML_LOG(ERROR) << "eglCreateSync get error" << err; + } +} + +void OHOSExternalTextureGL::WaitGPUFence(int fence_fd) { + EGLDisplay disp = eglGetCurrentDisplay(); + if (disp == EGL_NO_DISPLAY || !FdIsValid(fence_fd)) { + return; + } + if (FenceIsSignal(fence_fd)) { + // If the fence_fd is already signaled, it means the related data has + // already been produced, so there's no need to import it into OpenGL. + close(fence_fd); + return; + } + if (eglCreateSyncKHR_ != nullptr && eglWaitSyncKHR_ != nullptr && + eglDestroySyncKHR_ != nullptr) { + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fence_fd, EGL_NONE}; + EGLSyncKHR fence_sync = + eglCreateSyncKHR_(disp, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (fence_sync != EGL_NO_SYNC_KHR) { + eglWaitSyncKHR_(disp, fence_sync, 0); + gl_resources_[now_key_].wait_sync = UniqueEGLSync(fence_sync); + } else { + // eglDestroySync will close the fence_fd. + close(fence_fd); + } + } else { + close(fence_fd); + } + + EGLenum err = eglGetError(); + if (err != EGL_SUCCESS) { + FML_LOG(ERROR) << "eglWaitSync get error" << err; + } +} + +void OHOSExternalTextureGL::GPUResourceDestroy() { + gl_resources_.clear(); + // here we should have context. + GLenum err = glGetError(); + if (err != GL_NO_ERROR) { + FML_LOG(ERROR) << "GPUResourceDestroy get gl error:" << glGetError(); + } +} + +sk_sp OHOSExternalTextureGL::CreateDlImage( + PaintContext& context, + const SkRect& bounds, + NativeBufferKey key, + OHNativeWindowBuffer* nw_buffer) { + GLuint texture_name = 0; + glGenTextures(1, &texture_name); + impeller::UniqueGLTexture unique_texture(impeller::GLTexture{texture_name}); + OHOSUniqueEGLImageKHR unique_eglimage = CreateEGLImage(nw_buffer); + if (!unique_eglimage.is_valid() || texture_name == 0) { + return nullptr; + } + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_name); + if (glEGLImageTargetTexture2DOES_ != nullptr) { + glEGLImageTargetTexture2DOES_(GL_TEXTURE_EXTERNAL_OES, + (GLeglImageOES)unique_eglimage.get().image); + } else { + FML_LOG(ERROR) << "get null glEGLImageTargetTexture2DOES"; + return nullptr; + } + GrGLTextureInfo textureInfo = { + GL_TEXTURE_EXTERNAL_OES, unique_texture.get().texture_name, GL_RGBA8_OES}; + auto backendTexture = + GrBackendTextures::MakeGL(1, 1, skgpu::Mipmapped::kNo, textureInfo); + gl_resources_[key] = GlResource{std::move(unique_eglimage), + std::move(unique_texture), UniqueEGLSync()}; + + sk_sp image = SkImages::BorrowTextureFrom( + context.gr_context, backendTexture, kTopLeft_GrSurfaceOrigin, + kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); + sk_sp dl_image = DlImage::Make(image); + + // lru: oldest resource need earse + now_key_ = key; + gl_resources_.erase(image_lru_.AddImage(dl_image, key)); + return dl_image; +} + +OHOSUniqueEGLImageKHR OHOSExternalTextureGL::CreateEGLImage( + OHNativeWindowBuffer* nw_buffer) { + EGLDisplay disp = eglGetCurrentDisplay(); + if (disp == EGL_NO_DISPLAY || nw_buffer == nullptr) { + return OHOSUniqueEGLImageKHR(); + } + EGLint attrs[] = {EGL_IMAGE_PRESERVED, EGL_TRUE, EGL_NONE}; + + if (eglCreateImageKHR_ == nullptr) { + FML_LOG(ERROR) << "get null eglCreateImageKHR"; + return OHOSUniqueEGLImageKHR(); + } + + impeller::EGLImageKHRWithDisplay ohos_eglimage = + impeller::EGLImageKHRWithDisplay{ + eglCreateImageKHR_(disp, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_OHOS, + nw_buffer, attrs), + disp}; + EGLenum err = eglGetError(); + if (err != EGL_SUCCESS) { + FML_LOG(ERROR) << "eglCreateImageKHR get error" << err; + } + + return OHOSUniqueEGLImageKHR(ohos_eglimage); +} + +void OHOSExternalTextureGL::InitEGLFunPtr() { + static void* handle = dlopen("libEGL.so", RTLD_NOW); + // if we use eglGetProcAddress, we may get the libhvgr.so's func address. + // But normal egl func address(from libEGL.so) is pointed to a egl wrapper + // layer. Their parameters, despite having the same type name, refer to + // different underlying data structures and are not interchangeable(such as + // EGLDisplay). So we get address from dlsym first, if not then + // eglGetProcAddress. + if (eglCreateSyncKHR_ == nullptr) { + eglCreateSyncKHR_ = + (PFNEGLCREATESYNCKHRPROC)dlsym(handle, "eglCreateSyncKHR"); + if (eglCreateSyncKHR_ != nullptr) { + eglCreateSyncKHR_ = + (PFNEGLCREATESYNCKHRPROC)eglGetProcAddress("eglCreateSyncKHR"); + } + } + if (eglDupNativeFenceFDANDROID_ == nullptr) { + eglDupNativeFenceFDANDROID_ = (PFNEGLDUPNATIVEFENCEFDANDROIDPROC)dlsym( + handle, "eglDupNativeFenceFDANDROID"); + + if (eglDupNativeFenceFDANDROID_ == nullptr) { + eglDupNativeFenceFDANDROID_ = + (PFNEGLDUPNATIVEFENCEFDANDROIDPROC)eglGetProcAddress( + "eglDupNativeFenceFDANDROID"); + } + } + if (eglDestroySyncKHR_ == nullptr) { + eglDestroySyncKHR_ = + (PFNEGLDESTROYSYNCKHRPROC)dlsym(handle, "eglDestroySyncKHR"); + if (eglDestroySyncKHR_ == nullptr) { + eglDestroySyncKHR_ = + (PFNEGLDESTROYSYNCKHRPROC)eglGetProcAddress("eglDestroySyncKHR"); + } + } + if (eglWaitSyncKHR_ == nullptr) { + eglWaitSyncKHR_ = (PFNEGLWAITSYNCKHRPROC)dlsym(handle, "eglWaitSyncKHR"); + if (eglWaitSyncKHR_ == nullptr) { + eglWaitSyncKHR_ = + (PFNEGLWAITSYNCKHRPROC)eglGetProcAddress("eglWaitSyncKHR"); + } + } + if (eglCreateImageKHR_ == nullptr) { + eglCreateImageKHR_ = + (PFNEGLCREATEIMAGEKHRPROC)dlsym(handle, "eglCreateImageKHR"); + if (eglCreateImageKHR_ == nullptr) { + eglCreateImageKHR_ = + (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR"); + } + } + if (eglDestroyImageKHR_ == nullptr) { + eglDestroyImageKHR_ = + (PFNEGLDESTROYIMAGEKHRPROC)dlsym(handle, "eglDestroyImageKHR"); + if (eglDestroyImageKHR_ == nullptr) { + eglDestroyImageKHR_ = + (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR"); + } + } + if (glEGLImageTargetTexture2DOES_ == nullptr) { + glEGLImageTargetTexture2DOES_ = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)dlsym( + handle, "glEGLImageTargetTexture2DOES"); + if (glEGLImageTargetTexture2DOES_ == nullptr) { + glEGLImageTargetTexture2DOES_ = + (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress( + "glEGLImageTargetTexture2DOES"); + } + } +} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/ohos_external_texture_gl.h b/shell/platform/ohos/ohos_external_texture_gl.h new file mode 100644 index 0000000000000000000000000000000000000000..6c2b1a14335d31cb60a8784862d8bf20749cc72c --- /dev/null +++ b/shell/platform/ohos/ohos_external_texture_gl.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_EXTERNAL_TEXTURE_GL_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_EXTERNAL_TEXTURE_GL_H_ + +#include "ohos_external_texture.h" + +#include "flutter/impeller/toolkit/egl/image.h" +#include "flutter/impeller/toolkit/gles/texture.h" + +namespace flutter { + +struct OHOSEGLImageKHRWithDisplayTraits; +struct EGLSyncKHRTraits; +struct GlResource; + +using OHOSUniqueEGLImageKHR = + fml::UniqueObject; +using UniqueEGLSync = fml::UniqueObject; + +class OHOSExternalTextureGL : public OHOSExternalTexture { + public: + explicit OHOSExternalTextureGL(int64_t id, + OH_OnFrameAvailableListener listener); + + ~OHOSExternalTextureGL() override; + + static PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR_; + static PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID_; + static PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR_; + static PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR_; + static PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR_; + static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES_; + static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR_; + + static void InitEGLFunPtr(); + + protected: + void SetGPUFence(OHNativeWindowBuffer* window_buffer, int* fence_fd) override; + void WaitGPUFence(int fence_fd) override; + void GPUResourceDestroy() override; + + sk_sp CreateDlImage( + PaintContext& context, + const SkRect& bounds, + NativeBufferKey key, + OHNativeWindowBuffer* nw_buffer) override; + + private: + std::unordered_map gl_resources_; + NativeBufferKey now_key_; + + // void UpdateTransform(); + OHOSUniqueEGLImageKHR CreateEGLImage(OHNativeWindowBuffer* nw_buffer); + + FML_DISALLOW_COPY_AND_ASSIGN(OHOSExternalTextureGL); +}; + +// ohos' sdk don't have eglDestroyImageKHR symbol, so we manually get the +// eglDestroyImageKHR address. +struct OHOSEGLImageKHRWithDisplayTraits { + static impeller::EGLImageKHRWithDisplay InvalidValue() { + return {EGL_NO_IMAGE_KHR, EGL_NO_DISPLAY}; + } + + static bool IsValid(const impeller::EGLImageKHRWithDisplay& value) { + return value != InvalidValue(); + } + + static void Free(impeller::EGLImageKHRWithDisplay image) { + if (OHOSExternalTextureGL::eglDestroyImageKHR_) { + OHOSExternalTextureGL::eglDestroyImageKHR_(image.display, image.image); + } + } +}; + +struct EGLSyncKHRTraits { + static EGLSyncKHR InvalidValue() { return EGL_NO_SYNC_KHR; } + + static bool IsValid(const EGLSyncKHR& value) { + return value != InvalidValue(); + } + + static void Free(EGLSyncKHR sync) { + if (OHOSExternalTextureGL::eglDestroySyncKHR_) { + EGLDisplay disp = eglGetCurrentDisplay(); + OHOSExternalTextureGL::eglDestroySyncKHR_(disp, sync); + } + } +}; + +struct GlResource { + OHOSUniqueEGLImageKHR egl_image; + impeller::UniqueGLTexture texture; + UniqueEGLSync wait_sync; + UniqueEGLSync signal_sync; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_EXTERNAL_TEXTURE_GL_H_ \ No newline at end of file diff --git a/shell/platform/ohos/ohos_external_texture_vulkan.cpp b/shell/platform/ohos/ohos_external_texture_vulkan.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c5e19c7b170660ac0c5d1b523061a6b01b361b00 --- /dev/null +++ b/shell/platform/ohos/ohos_external_texture_vulkan.cpp @@ -0,0 +1,288 @@ +#include "ohos_external_texture_vulkan.h" +#include +#include +#include +#include +#include +#include +#include + +#include "flutter/impeller/core/formats.h" +#include "flutter/impeller/core/texture_descriptor.h" +#include "flutter/impeller/display_list/dl_image_impeller.h" +#include "flutter/impeller/renderer/backend/vulkan/command_buffer_vk.h" +#include "flutter/impeller/renderer/backend/vulkan/command_encoder_vk.h" +#include "flutter/impeller/renderer/backend/vulkan/ohos/ohb_texture_source_vk.h" +#include "flutter/impeller/renderer/backend/vulkan/texture_vk.h" +#include "fml/logging.h" +#include "impeller/renderer/backend/vulkan/fence_waiter_vk.h" +#include "vulkan/vulkan_core.h" + +namespace flutter { + +OHOSExternalTextureVulkan::OHOSExternalTextureVulkan( + const std::shared_ptr& impeller_context, + int64_t id, + OH_OnFrameAvailableListener listener) + : OHOSExternalTexture(id, listener), impeller_context_(impeller_context) {} + +OHOSExternalTextureVulkan::~OHOSExternalTextureVulkan() {} + +void OHOSExternalTextureVulkan::SetGPUFence(OHNativeWindowBuffer* window_buffer, + int* fence_fd) { + /// We cannot move the logic for generating fence_fd into the fence_callback + /// of QueueSubmit because Vulkan's task reordering means that + /// submitting a rendering task doesn’t guarantee its execution before the + /// semaphore specified by QueueSignalReleaseImageOHOS. This could cause + /// synchronization issues. To address this, the SetGPUFence call is adjusted + /// to trigger only when the fence_fd is actually needed—specifically, after + /// presenting the frame that uses the buffer. This ensures that the semaphore + /// is triggered after its corresponding rendering task. + + if (fence_fd == nullptr || window_buffer == nullptr) { + return; + } + if (FdIsValid(*fence_fd)) { + close(*fence_fd); + } + *fence_fd = -1; + + OH_NativeBuffer* native_buffer = nullptr; + int ret = + OH_NativeBuffer_FromNativeWindowBuffer(window_buffer, &native_buffer); + if (ret != 0 || native_buffer == nullptr) { + FML_LOG(ERROR) << "OHOSExternalTextureVulkan get OH_NativeBuffer error:" + << ret; + return; + } + + // ensure buffer_id > 0 (may get seqNum = 0) + uint32_t buffer_id = OH_NativeBuffer_GetSeqNum(native_buffer) + 1; + auto texture = vk_resources_[buffer_id].texture; + auto device = impeller_context_->GetDevice(); + if (texture && device) { + impeller::vk::ExportSemaphoreCreateInfo export_info; + export_info.setHandleTypes( + impeller::vk::ExternalSemaphoreHandleTypeFlagBits::eSyncFd); + + impeller::vk::SemaphoreCreateInfo create_info; + create_info.setPNext(&export_info); + auto ret = device.createSemaphoreUnique(create_info); + if (ret.result != impeller::vk::Result::eSuccess || !ret.value) { + FML_LOG(ERROR) << "createSemaphoreUnique in SetGPUFence failed"; + return; + } + + // Use it to ensure that when the window_buffer is held by the producer, the + // corresponding vksemaphore associated with the fence_fd will not be + // destroyed. + vk_resources_[buffer_id].signal_semaphore = std::move(ret.value); + + std::vector semaphore_vector; + semaphore_vector.push_back(vk_resources_[buffer_id].signal_semaphore.get()); + + // The logic of vkQueueSignalReleaseImageOHOS includes adding the + // vkSemaphore to the GraphicQueue, so this vkSemaphore does not require an + // additional submission through vkQueueSubmit. When the GraphicQueue + // reaches this point, the vkSemaphore and fence_fd will be signaled. + auto result = + impeller_context_->GetGraphicsQueue()->QueueSignalReleaseImageOHOS( + semaphore_vector, + vk_resources_[buffer_id].texture->GetTextureSource()->GetImage(), + fence_fd); + if (result != impeller::vk::Result::eSuccess) { + FML_LOG(ERROR) << "Could not QueueSignalReleaseImageOHOS: " + << impeller::vk::to_string(result) << " fence_fd " + << *fence_fd; + // Sometimes, it may return a valid file descriptor even if the call + // fails. + if (*fence_fd != -1 && FdIsValid(*fence_fd)) { + close(*fence_fd); + } + *fence_fd = -1; + return; + } + } + + bool is_signal = FenceIsSignal(*fence_fd); + bool fence_ok = FdIsValid(*fence_fd); + + // If the fd has already signaled, there is no need to send the fd to the + // producer, as the data has already been consumed. + if (fence_ok && is_signal) { + close(*fence_fd); + *fence_fd = -1; + } + + return; +} + +impeller::vk::UniqueSemaphore OHOSExternalTextureVulkan::CreateVkSemaphore( + int fence_fd) { + impeller::vk::SemaphoreCreateInfo semaphore_info; + impeller::vk::Device device = impeller_context_->GetDevice(); + auto semaphore_result = device.createSemaphoreUnique(semaphore_info, nullptr); + if (semaphore_result.result != impeller::vk::Result::eSuccess) { + FML_LOG(ERROR) << "vkCreateSemaphore failed"; + return impeller::vk::UniqueSemaphore(); + } + + auto semaphore = std::move(semaphore_result.value); + + impeller::vk::ImportSemaphoreFdInfoKHR import_info; + import_info.setSemaphore(semaphore.get()); + import_info.setFlags(impeller::vk::SemaphoreImportFlagBits::eTemporary); + import_info.setFd(fence_fd); + import_info.setHandleType( + impeller::vk::ExternalSemaphoreHandleTypeFlagBits::eSyncFd); + + auto import_result = device.importSemaphoreFdKHR(import_info); + if (import_result != impeller::vk::Result::eSuccess) { + FML_LOG(ERROR) << "importSemaphoreFdKHR failed"; + if (FdIsValid(fence_fd)) { + close(fence_fd); + } + return impeller::vk::UniqueSemaphore(); + } + return semaphore; +} + +void OHOSExternalTextureVulkan::WaitGPUFence(int fence_fd) { + auto texture = vk_resources_[now_key_].texture; + impeller::vk::SubmitInfo submit_info; + impeller::BarrierVK barrier; + std::shared_ptr cmd_buffer = + impeller_context_->CreateCommandBuffer(); + + std::function fence_callback = [fence_fd, cmd_buffer]() { + if (FdIsValid(fence_fd)) { + close(fence_fd); + } + }; + // auto close fd when error return. + fml::ScopedCleanupClosure auto_close(fence_callback); + + auto [fence_result, complete_fence] = + impeller_context_->GetDevice().createFenceUnique({}); + if (fence_result != impeller::vk::Result::eSuccess) { + FML_LOG(ERROR) << "Failed to create fence: " + << impeller::vk::to_string(fence_result); + return; + } + + if (texture) { + // Transition the layout to shader read. + impeller::CommandBufferVK& buffer_vk = + impeller::CommandBufferVK::Cast(*cmd_buffer); + auto encoder = buffer_vk.GetEncoder(); + + barrier.cmd_buffer = encoder->GetCommandBuffer(); + barrier.src_access = impeller::vk::AccessFlagBits::eColorAttachmentWrite | + impeller::vk::AccessFlagBits::eTransferWrite; + barrier.src_stage = + impeller::vk::PipelineStageFlagBits::eColorAttachmentOutput | + impeller::vk::PipelineStageFlagBits::eTransfer; + barrier.dst_access = impeller::vk::AccessFlagBits::eShaderRead; + barrier.dst_stage = impeller::vk::PipelineStageFlagBits::eFragmentShader; + + barrier.new_layout = impeller::vk::ImageLayout::eShaderReadOnlyOptimal; + + if (!texture->SetLayout(barrier)) { + FML_LOG(ERROR) << "External texture SetLayout failed"; + return; + } + + if (!encoder->EndCommandBuffer()) { + FML_LOG(ERROR) << "Failed to end command buffer"; + return; + } + + submit_info.setCommandBuffers(barrier.cmd_buffer); + } + + if (fence_fd > 0 && FdIsValid(fence_fd)) { + // If the fence_fd is already signaled, it means the related data has + // already been produced, so there's no need to import it into Vulkan. + if (!FenceIsSignal(fence_fd)) { + // The ownership of the fence_fd will be transferred to Vulkan. Once the + // corresponding wait signal is received, Vulkan will automatically + // release this fd. However, if this semaphore is not correctly submitted + // to the GraphicQueue, the fence_fd will not be triggered and will need + // to be released manually. + auto wait_semaphore = CreateVkSemaphore(fence_fd); + if (wait_semaphore.get() != VK_NULL_HANDLE) { + // We use shared_ptr to ensure that the semaphore can only be destroyed + // after Vulkan has finished using it (i.e., after the wait is + // complete). + auto shared_wait_semaphore = + std::make_shared( + std::move(wait_semaphore)); + impeller::vk::PipelineStageFlags wait_stage = + impeller::vk::PipelineStageFlagBits::eAllCommands; + submit_info.setWaitSemaphores(shared_wait_semaphore->get()); + submit_info.setWaitDstStageMask(wait_stage); + fence_callback = [shared_wait_semaphore, cmd_buffer]() { + // This lambda function will hold the semaphore and cmd_buffer until + // the signal is triggered, ensuring that the semaphore is destroyed + // only after Vulkan has finished using it. It is called on the + // fence-wait thread. + }; + } + } + } + + if (submit_info.waitSemaphoreCount == 0 && + submit_info.commandBufferCount == 0) { + FML_LOG(ERROR) << "Texture is null with fence fd " << fence_fd + << " :no need submit"; + return; + } + + auto status = impeller_context_->GetGraphicsQueue()->Submit(submit_info, + *complete_fence); + if (status != impeller::vk::Result::eSuccess) { + FML_LOG(ERROR) << "Failed to submit queue: " + << impeller::vk::to_string(status); + return; + } + + auto added_fence = impeller_context_->GetFenceWaiter()->AddFence( + std::move(complete_fence), fence_callback); + if (!added_fence) { + // only happen when the FenceWaiter thread is terminated. + FML_LOG(ERROR) << "failed to add Fence"; + return; + } + + auto_close.Release(); + return; +} + +void OHOSExternalTextureVulkan::GPUResourceDestroy() { + vk_resources_.clear(); +} + +sk_sp OHOSExternalTextureVulkan::CreateDlImage( + PaintContext& context, + const SkRect& bounds, + NativeBufferKey key, + OHNativeWindowBuffer* nw_buffer) { + auto texture_source = std::make_shared( + impeller_context_, nw_buffer); + if (!texture_source->IsValid()) { + FML_LOG(INFO) << " OHOSExternalTexture::CreateDlImage is null"; + return nullptr; + } + + auto texture = + std::make_shared(impeller_context_, texture_source); + + auto dl_image = impeller::DlImageImpeller::Make(texture); + + vk_resources_[key] = {.texture = texture}; + now_key_ = key; + vk_resources_.erase(image_lru_.AddImage(dl_image, key)); + return dl_image; +} + +} // namespace flutter diff --git a/shell/platform/ohos/ohos_external_texture_vulkan.h b/shell/platform/ohos/ohos_external_texture_vulkan.h new file mode 100644 index 0000000000000000000000000000000000000000..a2b273f35ccd06cd90c313f2640c9e561dbe1a59 --- /dev/null +++ b/shell/platform/ohos/ohos_external_texture_vulkan.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_EXTERNAL_TEXTURE_VULKAN_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_EXTERNAL_TEXTURE_VULKAN_H_ + +#include +#include "flutter/impeller/renderer/backend/vulkan/context_vk.h" +#include "impeller/renderer/backend/vulkan/texture_vk.h" +#include "ohos_external_texture.h" + +namespace flutter { + +struct VkResource; + +class OHOSExternalTextureVulkan : public OHOSExternalTexture { + public: + explicit OHOSExternalTextureVulkan( + const std::shared_ptr& impeller_context, + int64_t id, + OH_OnFrameAvailableListener listener); + + ~OHOSExternalTextureVulkan() override; + + protected: + std::unordered_map vk_resources_; + NativeBufferKey now_key_; + + void SetGPUFence(OHNativeWindowBuffer* window_buffer, int* fence_fd) override; + void WaitGPUFence(int fence_fd) override; + void GPUResourceDestroy() override; + + sk_sp CreateDlImage( + PaintContext& context, + const SkRect& bounds, + NativeBufferKey key, + OHNativeWindowBuffer* nw_buffer) override; + + private: + const std::shared_ptr impeller_context_; + impeller::vk::UniqueSemaphore CreateVkSemaphore(int fence_fd); + + FML_DISALLOW_COPY_AND_ASSIGN(OHOSExternalTextureVulkan); +}; + +struct VkResource { + std::shared_ptr texture; + // This is used to ensure that when the window_buffer is held by the producer, + // the corresponding vksemaphore associated with the fence_fd will not be + // destroyed. + impeller::vk::UniqueSemaphore signal_semaphore; +}; + +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_EXTERNAL_TEXTURE_VULKAN_H_ \ No newline at end of file diff --git a/shell/platform/ohos/ohos_image_generator.cpp b/shell/platform/ohos/ohos_image_generator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..50380c0f39574949a7222eba9136a03636896321 --- /dev/null +++ b/shell/platform/ohos/ohos_image_generator.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "ohos_image_generator.h" + +#include +#include +#include +#include +#include +#include + +#include +#include "fml/logging.h" +#include "fml/trace_event.h" +#include "include/core/SkAlphaType.h" +#include "include/core/SkColorType.h" +#include "include/core/SkImageInfo.h" +#include "third_party/skia/include/codec/SkCodecAnimation.h" + +namespace flutter { + +OHOSImageGenerator::OHOSImageGenerator(OH_ImageSourceNative* image_source) + : image_source_(image_source) { + OH_ImageSource_Info* info = nullptr; + OH_ImageSourceInfo_Create(&info); + if (info == nullptr) { + return; + } + uint32_t width = 0, height = 0; + OH_ImageSourceNative_GetImageInfo(image_source, 0, info); + OH_ImageSourceInfo_GetWidth(info, &width); + OH_ImageSourceInfo_GetHeight(info, &height); + OH_ImageSourceInfo_GetDynamicRange(info, &is_hdr_); + OH_ImageSourceInfo_Release(info); + origin_image_info_ = SkImageInfo::Make(width, height, kRGBA_8888_SkColorType, + kOpaque_SkAlphaType); + + // this is used for gif. + OH_ImageSourceNative_GetFrameCount(image_source, &frame_count_); + if (frame_count_ > 1) { + int* temp_delay_time = new int[frame_count_]; + OH_ImageSourceNative_GetDelayTimeList(image_source, temp_delay_time, + frame_count_); + frame_time_duration_.reserve(frame_count_); + frame_time_duration_.assign(temp_delay_time, + temp_delay_time + frame_count_); + FML_LOG(INFO) << "Create animated image generator frame:" << frame_count_ + << " duration:" << temp_delay_time[0]; + delete[] temp_delay_time; + } +} + +OHOSImageGenerator::~OHOSImageGenerator() { + if (image_source_) { + OH_ImageSourceNative_Release(image_source_); + } +} + +const SkImageInfo& OHOSImageGenerator::GetInfo() { + TRACE_EVENT1("flutter", "Image", "GetInfo", to_string().c_str()); + return origin_image_info_; +} + +unsigned int OHOSImageGenerator::GetFrameCount() const { + return frame_count_; +} + +unsigned int OHOSImageGenerator::GetPlayCount() const { + return frame_count_ == 1 ? 1 : kInfinitePlayCount; +} + +const ImageGenerator::FrameInfo OHOSImageGenerator::GetFrameInfo( + unsigned int frame_index) { + int32_t frame_duration = 0; + if (frame_index < frame_time_duration_.size()) { + frame_duration = frame_time_duration_[frame_index]; + } + if (frame_duration < 0) { + frame_duration = 0; + } + return {.required_frame = std::nullopt, + .duration = (uint32_t)frame_duration, + .disposal_method = SkCodecAnimation::DisposalMethod::kKeep}; +} + +SkISize OHOSImageGenerator::GetScaledDimensions(float desired_scale) { + // OHOS's PixelMap has the ability to automatically resize, and it will always + // return the requested size. + return {int(GetInfo().width() * desired_scale), + int(GetInfo().height() * desired_scale)}; +} + +bool OHOSImageGenerator::GetPixels(const SkImageInfo& info, + void* pixels, + size_t row_bytes, + unsigned int frame_index, + std::optional prior_frame) { + std::string trace_str = to_string() + "->" + std::to_string(info.width()) + + "*" + std::to_string(info.height()) + + "-index:" + std::to_string(frame_index); + TRACE_EVENT1("flutter", "Image", "GetPixelsOHOS", trace_str.c_str()); + if (frame_index == 0) { + FML_DLOG(INFO) << trace_str; + } + if (image_source_ == nullptr || info.colorType() != kRGBA_8888_SkColorType) { + FML_LOG(ERROR) << "invailed color type:" << std::to_string(info.colorType()) + << " " << to_string(); + return false; + } + + std::shared_ptr image_pixelmap = nullptr; + if (cached_pixelmaps_.find(frame_index) != cached_pixelmaps_.end()) { + // find pixelmap from the cache. + image_pixelmap = cached_pixelmaps_[frame_index]; + if ((int)image_pixelmap->width_ != info.width() || + (int)image_pixelmap->height_ != info.height()) { + // this cannot happen. + image_pixelmap = nullptr; + FML_LOG(WARNING) << "get changed size:" + << std::to_string(image_pixelmap->width_) << "*" + << std::to_string(image_pixelmap->height_) << "->" + << std::to_string(info.width()) << "*" + << std::to_string(info.height()); + } + } + + if (image_pixelmap == nullptr) { + image_pixelmap = CreatePixelMap(info.width(), info.height(), frame_index); + } + + if (image_pixelmap) { + uint32_t buffer_size = + image_pixelmap->width_ * image_pixelmap->height_ * RBGA8888_BYTES; + std::string trace_str = "size:" + std::to_string(buffer_size) + + "-stride:" + std::to_string(row_bytes); + TRACE_EVENT1("flutter", "Image", "ReadPixels", trace_str.c_str()); + if (frame_index == 0) { + FML_DLOG(INFO) << trace_str; + } + Image_ErrorCode err_code = + image_pixelmap->ReadPixels((uint8_t*)pixels, buffer_size, row_bytes); + if (err_code != IMAGE_SUCCESS) { + FML_LOG(ERROR) << "Pixelmap ReadPixels failed:" << err_code << " " + << to_string(); + return false; + } + if (image_pixelmap && frame_count_ > 1) { + // Cache animated images to improve performance. + cached_pixelmaps_[frame_index] = image_pixelmap; + } + return true; + } else { + return false; + } +} + +std::shared_ptr OHOSImageGenerator::MakeFromData( + sk_sp data) { + // Return directly if the image data is empty. + if (!data->data() || !data->size()) { + return nullptr; + } + TRACE_EVENT1("flutter", "Image", "MakeFromDataOHOS", + std::to_string(data->size()).c_str()); + + OH_ImageSourceNative* image_source = nullptr; + // The data will be coyied to ImageSourceNative. + // No modifications will be made to origin data. + Image_ErrorCode err_code = OH_ImageSourceNative_CreateFromData( + (uint8_t*)data->bytes(), data->size(), &image_source); + + if (err_code != IMAGE_SUCCESS || image_source == nullptr) { + FML_LOG(ERROR) << "Create ImageSource failed: " << err_code; + return nullptr; + } + + std::shared_ptr generator( + new OHOSImageGenerator(image_source)); + + if (generator->IsValidImageData()) { + return generator; + } else { + FML_LOG(ERROR) << "Invalid ImageData:" << generator->to_string(); + return nullptr; + } +} + +std::shared_ptr +OHOSImageGenerator::CreatePixelMap(int width, int height, int frame_index) { + OH_DecodingOptions* opts = nullptr; + Image_ErrorCode err_code = OH_DecodingOptions_Create(&opts); + if (err_code != IMAGE_SUCCESS || opts == nullptr) { + FML_LOG(ERROR) << "Create DecodingOptions failed:" << err_code; + return nullptr; + } + + Image_Size size = {(uint32_t)width, (uint32_t)height}; + OH_DecodingOptions_SetDesiredSize(opts, &size); + OH_DecodingOptions_SetPixelFormat(opts, PIXEL_FORMAT_RGBA_8888); + + // HDR requires the RGBA1010102 format and will need future support. + OH_DecodingOptions_SetDesiredDynamicRange(opts, IMAGE_DYNAMIC_RANGE_SDR); + OH_DecodingOptions_SetIndex(opts, frame_index); + + OH_PixelmapNative* pixelmap = nullptr; + // This could be time-consuming. + err_code = + OH_ImageSourceNative_CreatePixelmap(image_source_, opts, &pixelmap); + if (pixelmap && err_code == IMAGE_SUCCESS) { + auto image_pixelmap = std::make_shared(pixelmap); + FML_LOG(INFO) << "Create Pixelmap size:" + << std::to_string(image_pixelmap->width_) << "*" + << std::to_string(image_pixelmap->height_) << " stride " + << std::to_string(image_pixelmap->row_stride_) << " format " + << std::to_string(image_pixelmap->pixel_format_); + return image_pixelmap; + } else { + FML_LOG(ERROR) << "Create Pixelmap from Image source failed:" << err_code + << " request size:" << std::to_string(width) << "*" + << std::to_string(height) << " " << to_string(); + if (pixelmap) { + OH_PixelmapNative_Release(pixelmap); + } + return nullptr; + } +} + +bool OHOSImageGenerator::IsValidImageData() { + return GetInfo().width() != 0 && GetInfo().height() != 0 && frame_count_ != 0; +} + +OHOSImageGenerator::PixelMapOHOS::PixelMapOHOS(OH_PixelmapNative* pixelmap) { + if (pixelmap == nullptr) { + return; + } + pixelmap_ = pixelmap; + OH_Pixelmap_ImageInfo* info = nullptr; + OH_PixelmapImageInfo_Create(&info); + if (info == nullptr) { + return; + } + OH_PixelmapNative_GetImageInfo(pixelmap, info); + OH_PixelmapImageInfo_GetWidth(info, &width_); + OH_PixelmapImageInfo_GetHeight(info, &height_); + OH_PixelmapImageInfo_GetRowStride(info, &row_stride_); + OH_PixelmapImageInfo_GetPixelFormat(info, &pixel_format_); + OH_PixelmapImageInfo_Release(info); +} + +Image_ErrorCode OHOSImageGenerator::PixelMapOHOS::ReadPixels( + uint8_t* dst_buffer, + uint32_t buffer_size, + uint32_t row_stride) { + if (pixelmap_ == nullptr || row_stride < width_ * RBGA8888_BYTES) { + return IMAGE_BAD_PARAMETER; + } + Image_ErrorCode ret_code = IMAGE_SUCCESS; + uint8_t* temp_dst_buffer = dst_buffer; + if (row_stride > width_ * RBGA8888_BYTES) { + temp_dst_buffer = new uint8_t[buffer_size]; + } + if (temp_dst_buffer != NULL) { + size_t dst_size = buffer_size; + ret_code = + OH_PixelmapNative_ReadPixels(pixelmap_, temp_dst_buffer, &dst_size); + } + if (row_stride > width_ * RBGA8888_BYTES && temp_dst_buffer != nullptr) { + if (ret_code == IMAGE_SUCCESS) { + for (int i = 0; i < int(height_); i++) { + memcpy(dst_buffer + row_stride * i, + temp_dst_buffer + (width_ * RBGA8888_BYTES) * i, + width_ * RBGA8888_BYTES); + } + } + delete[] temp_dst_buffer; + } + return ret_code; +} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/ohos_image_generator.h b/shell/platform/ohos/ohos_image_generator.h new file mode 100755 index 0000000000000000000000000000000000000000..6004d0063b27cc79ad19c59715b2c03b22f11f1c --- /dev/null +++ b/shell/platform/ohos/ohos_image_generator.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_IMAGE_GENERATOR_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_IMAGE_GENERATOR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "flutter/lib/ui/painting/image_generator.h" +#include "include/core/SkRefCnt.h" + +#define RBGA8888_BYTES 4 + +namespace flutter { + +class OHOSImageGenerator : public ImageGenerator { + private: + explicit OHOSImageGenerator(OH_ImageSourceNative* image_source); + + public: + ~OHOSImageGenerator(); + + const SkImageInfo& GetInfo() override; + + unsigned int GetFrameCount() const override; + + unsigned int GetPlayCount() const override; + + const ImageGenerator::FrameInfo GetFrameInfo( + unsigned int frame_index) override; + + SkISize GetScaledDimensions(float desired_scale) override; + + bool GetPixels(const SkImageInfo& info, + void* pixels, + size_t row_bytes, + unsigned int frame_index, + std::optional prior_frame) override; + + static std::shared_ptr MakeFromData(sk_sp data); + + std::string to_string() const { + std::ostringstream oss; + oss << "ImageGenerate-" << reinterpret_cast(this) << ":(size-" + << origin_image_info_.width() << "*" << origin_image_info_.height() + << ",frame_count-" << frame_count_ << "-duration-" + << (frame_time_duration_.size() == 0 ? 0 : frame_time_duration_[0]) + << ",isHdr-" << is_hdr_ << ")"; + return oss.str(); + } + + private: + OH_ImageSourceNative* image_source_; + SkImageInfo origin_image_info_; + uint32_t frame_count_ = 0; + bool is_hdr_ = false; + std::vector frame_time_duration_; + + struct PixelMapOHOS { + OH_PixelmapNative* pixelmap_ = nullptr; + uint32_t width_ = 0; + uint32_t height_ = 0; + uint32_t row_stride_ = 0; + int32_t pixel_format_ = 0; + + explicit PixelMapOHOS(OH_PixelmapNative* pixelmap); + + ~PixelMapOHOS() { + if (pixelmap_) { + OH_PixelmapNative_Release(pixelmap_); + } + }; + + bool IsValid() { + return pixelmap_ != nullptr && width_ != 0 && height_ != 0; + }; + + Image_ErrorCode ReadPixels(uint8_t* dst_buffer, + uint32_t buffer_size, + uint32_t row_stride); + + PixelMapOHOS(const PixelMapOHOS&) = delete; + PixelMapOHOS& operator=(const PixelMapOHOS&) = delete; + }; + + std::map> cached_pixelmaps_; + + std::shared_ptr CreatePixelMap(int width, + int height, + int frame_index); + + bool IsValidImageData(); + + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(OHOSImageGenerator); +}; +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_IMAGE_GENERATOR_H_ \ No newline at end of file diff --git a/shell/platform/ohos/ohos_logger.c b/shell/platform/ohos/ohos_logger.c new file mode 100644 index 0000000000000000000000000000000000000000..95d0446d0e580112fe366cd8e87741d933539f30 --- /dev/null +++ b/shell/platform/ohos/ohos_logger.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "ohos_logger.h" + +#define OHOS_TAG "XComFlutterOHOS" + +int ohos_log(OhosLogLevel level, const char* fmt, ...) { + char buffer[1024]; + int len = 0; + { + va_list args; + va_start(args, fmt); + len = vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + va_end(args); + } + if (len <= 0) { + return len; + } + buffer[len] = '\0'; + // int OH_LOG_Print(LogType type, LogLevel level, unsigned int domain, const + // char *tag, const char *fmt, ...) + OH_LOG_Print(LOG_APP, (LogLevel)level, LOG_DOMAIN, OHOS_TAG, + "Thread:%{public}lu %{public}s", (unsigned long)pthread_self(), + buffer); + return len; +} diff --git a/shell/platform/ohos/ohos_logger.h b/shell/platform/ohos/ohos_logger.h new file mode 100644 index 0000000000000000000000000000000000000000..e4898424f8286cfd27d0f63d89f2463a4ca509da --- /dev/null +++ b/shell/platform/ohos/ohos_logger.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_LOGGER_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_LOGGER_H_ +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + /** Debug level to be used by {@link OH_LOG_DEBUG} */ + kOhosLogDebug = 3, + /** Informational level to be used by {@link OH_LOG_INFO} */ + kOhosLogInfo = 4, + /** Warning level to be used by {@link OH_LOG_WARN} */ + kOhosLogWarn = 5, + /** Error level to be used by {@link OH_LOG_ERROR} */ + kOhosLogError = 6, + /** Fatal level to be used by {@link OH_LOG_FATAL} */ + kOhosLogFatal = 7, +} OhosLogLevel; + +extern int ohos_log(OhosLogLevel level, const char* fmt, ...); + +#ifdef __cplusplus +} // end extern +#endif + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_LOGGER_H_ diff --git a/shell/platform/ohos/ohos_logging.h b/shell/platform/ohos/ohos_logging.h new file mode 100644 index 0000000000000000000000000000000000000000..148ac614848b05eb6641014743ed6dec794d08de --- /dev/null +++ b/shell/platform/ohos/ohos_logging.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_LOGGING_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_LOGGING_H_ + +#include +#define APP_LOG_DOMAIN 0x0000 +#define APP_LOG_TAG "XComFlutterOHOS_Native" + +#define LOGD(...) \ + ((void)OH_LOG_Print(LOG_APP, LOG_DEBUG, APP_LOG_DOMAIN, APP_LOG_TAG, \ + __VA_ARGS__)) + +#define LOGI(...) \ + ((void)OH_LOG_Print(LOG_APP, !(FML_LOG_IS_ON(INFO)) ? LOG_DEBUG : LOG_INFO, \ + APP_LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) + +#define LOGW(...) \ + ((void)OH_LOG_Print(LOG_APP, LOG_WARN, APP_LOG_DOMAIN, APP_LOG_TAG, \ + __VA_ARGS__)) + +#define LOGE(...) \ + ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, APP_LOG_DOMAIN, APP_LOG_TAG, \ + __VA_ARGS__)) + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_LOGGING_H_ diff --git a/shell/platform/ohos/ohos_main.cpp b/shell/platform/ohos/ohos_main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d337dc429754bf92cc73edfe4ccd31e53e4e594 --- /dev/null +++ b/shell/platform/ohos/ohos_main.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/ohos_main.h" + +#include "common/settings.h" +#include "flutter/fml/command_line.h" +#include "flutter/fml/file.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/message_loop.h" +#include "flutter/fml/native_library.h" +#include "flutter/fml/paths.h" +#include "flutter/fml/platform/ohos/napi_util.h" +#include "flutter/fml/platform/ohos/paths_ohos.h" +#include "flutter/fml/size.h" +#include "flutter/lib/ui/plugins/callback_cache.h" +#include "flutter/runtime/dart_vm.h" +#include "flutter/runtime/ptrace_check.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/common/switches.h" +#include "ohos_logging.h" +#include "third_party/dart/runtime/include/dart_tools_api.h" +#include "third_party/skia/include/core/SkFontMgr.h" + +namespace flutter { + +std::vector StringArrayToVector(napi_env env, + napi_value arrayValue) { + napi_status status; + uint32_t arrayLength = 0; + status = napi_get_array_length(env, arrayValue, &arrayLength); + LOGD("StringArrayToVector get array length %{pubilc}d", arrayLength); + if (status != napi_ok) { + LOGE("StringArrayToVector napi_get_array_length error "); + } + std::vector stringArray; + for (uint32_t i = 0; i < arrayLength; i++) { + napi_value elementValue; + status = napi_get_element(env, arrayValue, i, &elementValue); + if (status != napi_ok) { + LOGE("StringArrayToVector napi_get_element error"); + } + size_t stringLength; + status = napi_get_value_string_utf8(env, elementValue, nullptr, 0, + &stringLength); + if (status != napi_ok) { + LOGE("StringArrayToVector napi_get_value_string_utf8 error"); + } + + std::string stringValue(stringLength, '\0'); + status = napi_get_value_string_utf8(env, elementValue, &stringValue[0], + stringLength + 1, nullptr); + if (status != napi_ok) { + LOGE("StringArrayToVector napi_get_value_string_utf8 error"); + } + stringArray.push_back(stringValue); + } + return stringArray; +} + +OhosMain::OhosMain(const flutter::Settings& settings) + : settings_(settings), observatory_uri_callback_() {} + +OhosMain::~OhosMain() = default; + +static std::unique_ptr g_flutter_main; +static std::string productModel_; + +OhosMain& OhosMain::Get() { + FML_CHECK(g_flutter_main) << "ensureInitializationComplete must have already " + "been called."; + return *g_flutter_main; +} + +const flutter::Settings& OhosMain::GetSettings() const { + return settings_; +} + +/** + * @brief + * @note + * @param context: common.Context, args: Array, bundlePath: string, + * appStoragePath: string, engineCachesPath: string, initTimeMillis: number + * @return napi_value + */ +napi_value OhosMain::Init(napi_env env, napi_callback_info info) { + size_t argc = 7; + napi_value param[7]; + std::string kernelPath, appStoragePath, engineCachesPath; + int64_t initTimeMillis; + std::string productModel; + napi_get_cb_info(env, info, &argc, param, nullptr, nullptr); + napi_get_value_int64(env, param[5], &initTimeMillis); + fml::napi::GetString(env, param[2], kernelPath); + fml::napi::GetString(env, param[3], appStoragePath); + fml::napi::GetString(env, param[4], engineCachesPath); + fml::napi::GetString(env, param[6], productModel); + productModel_ = productModel; + + FML_DLOG(INFO) << "INIT kernelPath:" << kernelPath; + FML_DLOG(INFO) << "appStoragePath:" << appStoragePath; + FML_DLOG(INFO) << "engineCachesPath:" << engineCachesPath; + FML_DLOG(INFO) << "initTimeMillis:" << initTimeMillis; + FML_DLOG(INFO) << "productModel:" << productModel; + + std::vector args; + args.push_back("flutter"); + fml::napi::GetArrayString(env, param[1], args); + for (std::vector::iterator it = args.begin(); it != args.end(); + ++it) { + FML_DLOG(INFO) << *it; + } + auto command_line = fml::CommandLineFromIterators(args.begin(), args.end()); + auto settings = SettingsFromCommandLine(command_line); + flutter::DartCallbackCache::SetCachePath(appStoragePath); + fml::paths::InitializeOhosCachesPath(std::string(engineCachesPath)); + flutter::DartCallbackCache::LoadCacheFromDisk(); + + if (!flutter::DartVM::IsRunningPrecompiledCode() && kernelPath[0]) { + auto application_kernel_path = kernelPath; + if (fml::IsFile(application_kernel_path)) { + FML_DLOG(INFO) << "application_kernel_path exist"; + settings.application_kernel_asset = application_kernel_path; + } + } + + settings.task_observer_add = [](intptr_t key, const fml::closure& callback) { + FML_DLOG(INFO) << "task_observer_add:" << (int64_t)key; + fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback); + }; + settings.task_observer_remove = [](intptr_t key) { + FML_DLOG(INFO) << "task_observer_remove:" << (int64_t)key; + fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + }; + settings.log_message_callback = [](const std::string& tag, + const std::string& message) { + // The logs output here are very important for The Dart VM and cannot be + // deleted or blocked. + LOGW("%{public}s settings log message: %{public}s", tag.c_str(), + message.c_str()); + }; + + if (settings.enable_software_rendering) { + FML_CHECK(!settings.enable_impeller) + << "Impeller does not support software rendering. Either disable " + "software rendering or disable impeller."; + settings.ohos_rendering_api = OHOSRenderingAPI::kSoftware; + } + if (settings.enable_impeller) { + settings.ohos_rendering_api = OHOSRenderingAPI::kImpellerVulkan; + } else { + settings.ohos_rendering_api = OHOSRenderingAPI::kOpenGLES; + } + switch (settings.ohos_rendering_api) { + case OHOSRenderingAPI::kSoftware: + case OHOSRenderingAPI::kOpenGLES: + settings.enable_impeller = false; + break; + case OHOSRenderingAPI::kImpellerVulkan: + settings.enable_impeller = true; + break; + } + + if (!EnableTracingIfNecessary(settings)) { + LOGE( + "Cannot create a FlutterEngine instance in debug mode without Flutter " + "tooling.\n\n" + "To Launch in debug mode, run 'flutter run' from Flutter tools, run " + "from an IDE with a" + "Flutter IDE plugin.\nAlternatively profile and release mode apps " + "canbe launched from " + "the home screen."); + return nullptr; + } + + g_flutter_main.reset(new OhosMain(settings)); + // TODO : g_flutter_main->SetupObservatoryUriCallback(env); + LOGD("OhosMain::Init finished."); + napi_value result; + napi_create_int64(env, 0, &result); + return result; +} + +napi_value OhosMain::NativeInit(napi_env env, napi_callback_info info) { + napi_value result = OhosMain::Init(env, info); + return result; +} + +bool OhosMain::IsEmulator() { + return productModel_ == "emulator"; +} + +} // namespace flutter diff --git a/shell/platform/ohos/ohos_main.h b/shell/platform/ohos/ohos_main.h new file mode 100644 index 0000000000000000000000000000000000000000..5bb1be9c4eaa82e3f5adef82c350de735e80fc1c --- /dev/null +++ b/shell/platform/ohos/ohos_main.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_MAIN_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_MAIN_H_ +#define FML_USED_ON_EMBEDDER +#include "flutter/common/settings.h" +#include "flutter/fml/macros.h" +#include "flutter/runtime/dart_service_isolate.h" +#include "napi/native_api.h" +#include "napi_common.h" + +namespace flutter { +class OhosMain { + public: + ~OhosMain(); + static OhosMain& Get(); + const flutter::Settings& GetSettings() const; + static napi_value NativeInit(napi_env env, napi_callback_info info); + static bool IsEmulator(); + + private: + const flutter::Settings settings_; + DartServiceIsolate::CallbackHandle observatory_uri_callback_; + + explicit OhosMain(const flutter::Settings& settings); + + static napi_value Init(napi_env env, napi_callback_info info); + + void SetupObservatoryUriCallback(napi_env env, napi_callback_info info); + FML_DISALLOW_COPY_AND_ASSIGN(OhosMain); +}; +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_MAIN_H_ diff --git a/shell/platform/ohos/ohos_shell_holder.cpp b/shell/platform/ohos/ohos_shell_holder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dc8674758b9b43fe0b9f848c4e59c604d750feb3 --- /dev/null +++ b/shell/platform/ohos/ohos_shell_holder.cpp @@ -0,0 +1,754 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/ohos_shell_holder.h" +#include "flutter/fml/native_library.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/run_configuration.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/shell/platform/ohos/ohos_display.h" + +#include "flutter/shell/platform/ohos/ohos_logging.h" +#include "fml/trace_event.h" + +#include "third_party/skia/src/ports/skia_ohos/SkFontMgr_ohos.h" +#include "txt/platform.h" +#include "utils/ohos_utils.h" + +#include +#include +#include + +namespace flutter { + +std::string OHOSLastFontPath = ""; + +static void OHOSPlatformThreadConfigSetter( + const fml::Thread::ThreadConfig& config) { + fml::Thread::SetCurrentThreadName(config); + // set thread priority + switch (config.priority) { + case fml::Thread::ThreadPriority::kBackground: { + int ret = OH_QoS_SetThreadQoS(QoS_Level::QOS_BACKGROUND); + FML_DLOG(INFO) << "qos set background result:" << ret + << ",tid:" << gettid(); + break; + } + case fml::Thread::ThreadPriority::kDisplay: { + int ret = OH_QoS_SetThreadQoS(QoS_Level::QOS_USER_INTERACTIVE); + FML_DLOG(INFO) << "qos set display result:" << ret << ",tid:" << gettid(); + break; + } + case fml::Thread::ThreadPriority::kRaster: { + int ret = OH_QoS_SetThreadQoS(QoS_Level::QOS_USER_INTERACTIVE); + FML_DLOG(INFO) << "qos set raster result:" << ret << ",tid:" << gettid(); + break; + } + default: + int ret = OH_QoS_SetThreadQoS(QoS_Level::QOS_DEFAULT); + FML_DLOG(INFO) << "qos set default result:" << ret << ",tid:" << gettid(); + } +} + +static PlatformData GetDefaultPlatformData() { + FML_DLOG(INFO) << "GetDefaultPlatformData"; + PlatformData platform_data; + platform_data.lifecycle_state = "AppLifecycleState.detached"; + return platform_data; +} + +static bool EndsWith(std::string str, std::string suffix) { + if (str.length() < suffix.length()) { + return false; + } + return str.substr(str.length() - suffix.length()) == suffix; +} + +static std::string GetFontFileName(std::string path) { + std::string fontFamilyName = ""; + DIR* dir; + struct dirent* ent; + if ((dir = opendir(path.c_str())) == nullptr) { + return fontFamilyName; + } + while ((ent = readdir(dir)) != nullptr) { + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { + continue; + } + if (EndsWith(ent->d_name, ".ttf")) { + fontFamilyName = ent->d_name; + break; + } + } + closedir(dir); + return fontFamilyName; +} + +static bool IsFontDirValid(std::string path) { + DIR* dir; + struct dirent* ent; + bool isFlagFileExist = false; + bool isFontDirExist = false; + if ((dir = opendir(path.c_str())) == nullptr) { + if (errno == ENOENT) { + FML_DLOG(ERROR) << "ERROR ENOENT"; + } else if (errno == EACCES) { + FML_DLOG(ERROR) << "ERROR EACCES"; + } else { + FML_DLOG(ERROR) << "ERROR Other"; + } + return false; + } + while ((ent = readdir(dir)) != nullptr) { + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { + continue; + } + if (strcmp(ent->d_name, "flag") == 0) { + isFlagFileExist = true; + } else if (strcmp(ent->d_name, "fonts") == 0) { + isFontDirExist = true; + } + } + closedir(dir); + if (isFlagFileExist && isFontDirExist) { + FML_DLOG(INFO) << "font path exist" << path; + return true; + } + return false; +} + +static std::string CheckFontSource() { + std::string path = "/data/themes/a/app"; + if (!IsFontDirValid(path)) { + path = "/data/themes/b/app"; + if (!IsFontDirValid(path)) { + return ""; + } + } + path = path.append("/fonts/"); + std::string fileName = GetFontFileName(path); + if (fileName.empty()) { + return ""; + } + path = path.append(fileName); + if (OHOSLastFontPath.empty()) { + OHOSLastFontPath = path; + } + return path; +} + +static bool IsFontChanged(std::string currentPath) { + if (!currentPath.empty() && currentPath != OHOSLastFontPath) { + OHOSLastFontPath = currentPath; + return true; + } else { + return false; + } +} + +OHOSShellHolder::OHOSShellHolder( + const flutter::Settings& settings, + std::shared_ptr napi_facade, + void* platform_loop) + : settings_(settings), napi_facade_(napi_facade) { + FML_DLOG(INFO) << " ohos shell holder constructor"; + static size_t thread_host_count = 1; + auto thread_label = std::to_string(thread_host_count++); + TRACE_EVENT0("OHOSShellHolder", "Create"); + auto mask = + ThreadHost::Type::kUi | ThreadHost::Type::kRaster | ThreadHost::Type::kIo; + + flutter::ThreadHost::ThreadHostConfig host_config( + thread_label, mask, OHOSPlatformThreadConfigSetter); + host_config.ui_config = fml::Thread::ThreadConfig( + flutter::ThreadHost::ThreadHostConfig::MakeThreadName( + flutter::ThreadHost::Type::kUi, thread_label), + fml::Thread::ThreadPriority::kDisplay); + host_config.raster_config = fml::Thread::ThreadConfig( + flutter::ThreadHost::ThreadHostConfig::MakeThreadName( + flutter::ThreadHost::Type::kRaster, thread_label), + fml::Thread::ThreadPriority::kRaster); + host_config.io_config = fml::Thread::ThreadConfig( + flutter::ThreadHost::ThreadHostConfig::MakeThreadName( + flutter::ThreadHost::Type::kIo, thread_label), + fml::Thread::ThreadPriority::kNormal); + + thread_host_ = std::make_shared(host_config); + FML_DLOG(INFO) << "thred host config finish"; + fml::WeakPtr weak_platform_view; + + Shell::CreateCallback on_create_platform_view = + [&napi_facade, &weak_platform_view](Shell& shell) { + FML_DLOG(INFO) << "on_create_platform_view"; + std::unique_ptr platform_view_OHOS; + platform_view_OHOS = std::make_unique( + shell, // delegate + shell.GetTaskRunners(), // task runners + napi_facade, // napi interop + shell.GetSettings() + .enable_software_rendering, // use software rendering + shell.GetSettings().msaa_samples // msaa sample count + ); + LOGI("on_create_platform_view LOGI"); + FML_LOG(INFO) << "on_create_platform_view end"; + weak_platform_view = platform_view_OHOS->GetWeakPtr(); + LOGI("on_create_platform_view LOGI2"); + FML_LOG(INFO) << "on_create_platform_view end1"; + // std::vector> displays; + // displays.push_back(std::make_unique(napi_facade)); + // FML_DLOG(INFO) << "on_create_platform_view LOGI3"; + // FML_LOG(INFO) << "on_create_platform_view end3---here"; + // shell.OnDisplayUpdates(std::move(displays)); + LOGI("on_create_platform_view LOGI4"); + FML_LOG(INFO) << "on_create_platform_view end3"; + return platform_view_OHOS; + }; + + Shell::CreateCallback on_create_rasterizer = [](Shell& shell) { + FML_DLOG(INFO) << "Create Rasterizer Callback"; + return std::make_unique(shell); + }; + + // The current thread will be used as the platform thread. Ensure that the + // message loop is initialized. + fml::MessageLoop::EnsureInitializedForCurrentThread(platform_loop); + fml::RefPtr raster_runner; + fml::RefPtr ui_runner; + fml::RefPtr io_runner; + fml::RefPtr platform_runner = + fml::MessageLoop::GetCurrent().GetTaskRunner(); + raster_runner = thread_host_->raster_thread->GetTaskRunner(); + ui_runner = thread_host_->ui_thread->GetTaskRunner(); + io_runner = thread_host_->io_thread->GetTaskRunner(); + + flutter::TaskRunners task_runners(thread_label, // label + platform_runner, // platform + raster_runner, // raster + ui_runner, // ui + io_runner // io + ); + + napi_facade_->SetPlatformTaskRunner(platform_runner); + FML_DLOG(INFO) << "before shell create"; + shell_ = + Shell::Create(GetDefaultPlatformData(), // window data + task_runners, // task runners + settings_, // settings + on_create_platform_view, // platform view create callback + on_create_rasterizer // rasterizer create callback + ); + FML_DLOG(INFO) << "shell create end"; + if (shell_) { + shell_->GetDartVM()->GetConcurrentMessageLoop()->PostTaskToAllWorkers([]() { + if (::setpriority(PRIO_PROCESS, gettid(), 1) != 0) { + FML_DLOG(ERROR) << "Failed to set Workers task runner priority"; + } + }); + + LOGI("shell_ end"); + shell_->RegisterImageDecoder( + [](sk_sp buffer) { + return OHOSImageGenerator::MakeFromData(std::move(buffer)); + }, + // OHOS's PixelMap decoding supports hardware decoding, offering higher + // performance compared to Skia decoding. + 1); + + FML_DLOG(INFO) << "Registered ohos image decoder"; + } + + platform_view_ = weak_platform_view; + bridge_ = std::make_shared(); + bridge_mutex_ = std::make_shared(); + platform_view_->SetSemanticsBridge(bridge_, bridge_mutex_); + FML_DCHECK(platform_view_); +} + +OHOSShellHolder::OHOSShellHolder( + const Settings& settings, + const std::shared_ptr& napi_facade, + const std::shared_ptr& thread_host, + std::unique_ptr shell, + std::unique_ptr hap_asset_provider, + const fml::WeakPtr& platform_view) + : settings_(settings), + platform_view_(platform_view), + thread_host_(thread_host), + shell_(std::move(shell)), + asset_provider_(std::move(hap_asset_provider)), + napi_facade_(napi_facade) { + FML_DCHECK(napi_facade); + FML_DCHECK(shell_); + FML_DCHECK(shell_->IsSetup()); + FML_DCHECK(platform_view_); + FML_DCHECK(thread_host_); + bridge_ = std::make_shared(); + bridge_mutex_ = std::make_shared(); + platform_view_->SetSemanticsBridge(bridge_, bridge_mutex_); +} + +OHOSShellHolder::~OHOSShellHolder() { + FML_LOG(INFO) << "MHN enter ~OHOSShellHolder()"; + shell_.reset(); + thread_host_.reset(); + shell_ = nullptr; +} + +bool OHOSShellHolder::IsValid() const { + return shell_ != nullptr; +} + +const flutter::Settings& OHOSShellHolder::GetSettings() const { + return settings_; +} + +std::unique_ptr OHOSShellHolder::Spawn( + std::shared_ptr napi_facade, + const std::string& entrypoint, + const std::string& libraryUrl, + const std::string& initial_route, + const std::vector& entrypoint_args) const { + if (!IsValid()) { + FML_LOG(ERROR) << "A new Shell can only be spawned " + "if the current Shell is properly constructed"; + return nullptr; + } + + fml::WeakPtr weak_platform_view; + PlatformViewOHOS* ohos_platform_view = platform_view_.get(); + FML_DCHECK(ohos_platform_view); + std::shared_ptr ohos_context = + ohos_platform_view->GetOHOSContext(); + FML_DCHECK(ohos_context); + + Shell::CreateCallback on_create_platform_view = + [&napi_facade, ohos_context, &weak_platform_view](Shell& shell) { + std::unique_ptr platform_view_ohos; + platform_view_ohos = std::make_unique( + shell, // delegate + shell.GetTaskRunners(), // task runners + napi_facade, // NAPI interop + ohos_context // Ohos context + ); + weak_platform_view = platform_view_ohos->GetWeakPtr(); + return platform_view_ohos; + }; + + Shell::CreateCallback on_create_rasterizer = [](Shell& shell) { + return std::make_unique(shell); + }; + + auto config = BuildRunConfiguration(entrypoint, libraryUrl, entrypoint_args); + if (!config) { + // If the RunConfiguration was null, the kernel blob wasn't readable. + // Fail the whole thing. + return nullptr; + } + + std::unique_ptr shell = + shell_->Spawn(std::move(config.value()), initial_route, + on_create_platform_view, on_create_rasterizer); + + return std::unique_ptr(new OHOSShellHolder( + GetSettings(), napi_facade, thread_host_, std::move(shell), + asset_provider_->Clone(), weak_platform_view)); +} + +fml::WeakPtr OHOSShellHolder::GetPlatformView() { + FML_DCHECK(platform_view_); + return platform_view_; +} + +void OHOSShellHolder::NotifyLowMemoryWarning() { + if (!IsValid()) { + FML_LOG(ERROR) << "NotifyLowMemoryWarning, Is Not Valid"; + return; + } + shell_->NotifyLowMemoryWarning(); +} + +void OHOSShellHolder::Launch( + std::unique_ptr hap_asset_provider, + const std::string& entrypoint, + const std::string& libraryUrl, + const std::vector& entrypoint_args) { + FML_DLOG(INFO) << "Launch ...entrypoint<<" << entrypoint + << ",libraryUrl:" << libraryUrl; + if (!IsValid()) { + FML_LOG(ERROR) << "Launch, Is Not Valid"; + return; + } + + asset_provider_ = std::move(hap_asset_provider); + auto config = BuildRunConfiguration(entrypoint, libraryUrl, entrypoint_args); + if (!config) { + return; + } + std::vector> displays; + displays.push_back(std::make_unique(napi_facade_)); + FML_DLOG(INFO) << "on_create_platform_view LOGI3"; + FML_LOG(INFO) << "on_create_platform_view end3---here"; + shell_->OnDisplayUpdates(std::move(displays)); + FML_LOG(INFO) << "start RunEngine"; + shell_->RunEngine(std::move(config.value())); + FML_LOG(INFO) << "end Launch"; +} + +void OHOSShellHolder::InitializeSystemFont() { + SkFontMgr_OHOS* mgr = (SkFontMgr_OHOS*)(txt::GetDefaultFontManager().get()); + + std::string path = CheckFontSource(); + if (path.empty()) { + LOGE("system font file not found"); + return; + } + mgr->InitializeSystemFont(path); +} + +void OHOSShellHolder::ReloadSystemFonts() { + std::string currentPath = CheckFontSource(); + if (IsFontChanged(currentPath)) { + SkFontMgr_OHOS* mgr = (SkFontMgr_OHOS*)(txt::GetDefaultFontManager().get()); + mgr->AddSystemFont(currentPath); + shell_->ReloadSystemFonts(); + } +} + +std::optional OHOSShellHolder::BuildRunConfiguration( + const std::string& entrypoint, + const std::string& libraryUrl, + const std::vector& entrypoint_args) const { + std::unique_ptr isolate_configuration; + FML_DLOG(INFO) << "entrypoint." << entrypoint.c_str(); + FML_DLOG(INFO) << "libraryUrl." << libraryUrl.c_str(); + if (flutter::DartVM::IsRunningPrecompiledCode()) { + FML_LOG(INFO) << "isolate_configuration."; + isolate_configuration = IsolateConfiguration::CreateForAppSnapshot(); + } else { + std::unique_ptr kernel_blob = + fml::FileMapping::CreateReadOnly( + GetSettings().application_kernel_asset); + if (!kernel_blob) { + FML_DLOG(ERROR) << "Unable to load the kernel blob asset."; + return std::nullopt; + } + FML_LOG(INFO) << "CreateForKernel."; + isolate_configuration = + IsolateConfiguration::CreateForKernel(std::move(kernel_blob)); + } + + RunConfiguration config(std::move(isolate_configuration)); + config.AddAssetResolver(asset_provider_->Clone()); + + { + if (!entrypoint.empty() && !libraryUrl.empty()) { + FML_LOG(INFO) << "SetEntrypointAndLibrary."; + config.SetEntrypointAndLibrary(entrypoint, libraryUrl); + } else if (!entrypoint.empty()) { + FML_LOG(INFO) << "SetEntrypoint."; + config.SetEntrypoint(entrypoint); + } + if (!entrypoint_args.empty()) { + FML_LOG(INFO) << "SetEntrypointArgs."; + config.SetEntrypointArgs(entrypoint_args); + } + } + return config; +} + +static ACTIONS_ ArkuiActionsToFlutterActions( + ArkUI_Accessibility_ActionType arkui_action, + SemanticsNodeExtend* node) { + switch (arkui_action) { + case ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLICK: + return ACTIONS_::kTap; + + case ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_LONG_CLICK: + return ACTIONS_::kLongPress; + + case ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_FORWARD: + if (node->HasAction(SemanticsAction::kScrollUp)) { + return ACTIONS_::kScrollUp; + } else if (node->HasAction(SemanticsAction::kScrollLeft)) { + return ACTIONS_::kScrollLeft; + } else if (node->HasAction(SemanticsAction::kIncrease)) { + return ACTIONS_::kIncrease; + } else { + return ACTIONS_::kCustomAction; + } + + case ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_BACKWARD: + if (node->HasAction(SemanticsAction::kScrollDown)) { + return ACTIONS_::kScrollDown; + } else if (node->HasAction(SemanticsAction::kScrollRight)) { + return ACTIONS_::kScrollRight; + } else if (node->HasAction(SemanticsAction::kDecrease)) { + return ACTIONS_::kDecrease; + } else { + return ACTIONS_::kCustomAction; + } + + case ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_COPY: + return ACTIONS_::kCopy; + + case ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CUT: + return ACTIONS_::kCut; + + case ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_PASTE: + return ACTIONS_::kPaste; + + case ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_GAIN_ACCESSIBILITY_FOCUS: + return ACTIONS_::kDidGainAccessibilityFocus; + + case ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLEAR_ACCESSIBILITY_FOCUS: + return ACTIONS_::kDidLoseAccessibilityFocus; + + case ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SELECT_TEXT: + return ACTIONS_::kSetSelection; + + case ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SET_TEXT: + return ACTIONS_::kSetText; + + default: + // might not match to the valid action in arkui + return ACTIONS_::kCustomAction; + } +} + +static const char* ARKUI_ACTION_ARG_SET_TEXT = "setText"; +static const char* ARKUI_ACTION_ARG_SELECT_TEXT_START = "selectTextBegin"; +static const char* ARKUI_ACTION_ARG_SELECT_TEXT_END = "selectTextEnd"; +static const char* ACTION_ARGU_SET_OFFSET = "offset"; + +static std::vector GetAccessibilitySelectText( + ArkUI_AccessibilityActionArguments* args, + SemanticsNodeExtend* node) { + char* textSelectBase = nullptr; + OH_ArkUI_FindAccessibilityActionArgumentByKey( + args, ARKUI_ACTION_ARG_SELECT_TEXT_START, &textSelectBase); + if (textSelectBase == nullptr) { + LOGE("PerformSelectText -> textSelectBase get null value"); + return {}; + } + + char* textSelectExtent = nullptr; + OH_ArkUI_FindAccessibilityActionArgumentByKey( + args, ARKUI_ACTION_ARG_SELECT_TEXT_END, &textSelectExtent); + if (textSelectExtent == nullptr) { + LOGE("PerformSelectText -> textSelectExtent get null value"); + return {}; + } + + std::map selectionMap; + bool hasSelected = args != nullptr && textSelectBase != nullptr && + textSelectExtent != nullptr; + if (hasSelected) { + int32_t base; + int32_t extent; + OHOSUtils::CharArrayToInt32(textSelectBase, base); + OHOSUtils::CharArrayToInt32(textSelectExtent, extent); + selectionMap.insert({"base", base}); + selectionMap.insert({"extent", extent}); + node->textSelectionBase = base; + node->textSelectionExtent = extent; + } else { + selectionMap.insert( + {ARKUI_ACTION_ARG_SELECT_TEXT_START, node->textSelectionBase}); + selectionMap.insert( + {ARKUI_ACTION_ARG_SELECT_TEXT_END, node->textSelectionExtent}); + } + + // serialize map to byte vector + std::vector encodedData = + OHOSUtils::SerializeStringIntMap(selectionMap); + return encodedData; +} + +static std::vector GetAccessibilitySetText( + ArkUI_AccessibilityActionArguments* args, + SemanticsNodeExtend* node) { + char* newText = nullptr; + OH_ArkUI_FindAccessibilityActionArgumentByKey(args, ARKUI_ACTION_ARG_SET_TEXT, + &newText); + if (newText == nullptr) { + LOGE( + "PerformSetText -> OH_ArkUI_FindAccessibilityActionArgumentByKey get " + "null value"); + return {}; + } + node->value = newText; + node->valueAttributes = {}; + return std::vector(newText, newText + strlen(newText)); +} + +void OHOSShellHolder::SetAccessibilityProvider( + ArkUI_AccessibilityProvider* provider) { + LOGD("SetAccessibilityProvider %{public}p", provider); + std::lock_guard lock(*bridge_mutex_); + if (bridge_->provider_ohos_) { + bridge_->old_provider_ohos_ = bridge_->provider_ohos_; + } + bridge_->provider_ohos_ = provider; +} + +int32_t OHOSShellHolder::FindFocusNode(int32_t id, + ArkUI_AccessibilityFocusType focusType, + ArkUI_AccessibilityElementInfo* info) { + std::lock_guard lock(*bridge_mutex_); + return bridge_->FindFocusNode(id, focusType, info); +} + +int32_t OHOSShellHolder::FindNextFocusNode( + int32_t id, + ArkUI_AccessibilityFocusMoveDirection direction, + ArkUI_AccessibilityElementInfo* info) { + std::lock_guard lock(*bridge_mutex_); + return bridge_->FindNextFocusNode(id, direction, info); +} + +int32_t OHOSShellHolder::FillNodesWithSearchText( + int32_t id, + const char* text, + ArkUI_AccessibilityElementInfoList* list) { + std::lock_guard lock(*bridge_mutex_); + return bridge_->FillNodesWithSearchText(id, text, list); + ; +} + +int32_t OHOSShellHolder::FillNodesWithSearch( + int32_t id, + ArkUI_AccessibilitySearchMode mode, + ArkUI_AccessibilityElementInfoList* list) { + std::lock_guard lock(*bridge_mutex_); + return bridge_->FillNodesWithSearch(id, mode, list); +} + +int32_t OHOSShellHolder::ExecuteAction( + int64_t elementId, + ArkUI_Accessibility_ActionType action, + ArkUI_AccessibilityActionArguments* actionArguments) { + std::lock_guard lock(*bridge_mutex_); + if (bridge_->provider_ohos_ == nullptr) { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } + + auto node = bridge_->GetNodeById((int32_t)elementId); + if (!node) { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } + + FML_DLOG(INFO) << "ExecuteAction: " << elementId << " action:" << action; + + std::string trace_str = + "id-" + std::to_string(elementId) + "-action-" + std::to_string(action); + TRACE_EVENT1("flutter", "ExecuteAction", "action", trace_str.c_str()); + + std::vector flutter_args; + if (action == ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SELECT_TEXT) { + flutter_args = GetAccessibilitySelectText(actionArguments, node); + } else if (action == ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SET_TEXT) { + flutter_args = GetAccessibilitySetText(actionArguments, node); + } + + auto flutter_action = ArkuiActionsToFlutterActions(action, node); + + fml::TaskRunner::RunNowOrPostTask( + shell_->GetTaskRunners().GetPlatformTaskRunner(), + [this, id = elementId, flutter_action, action, + args = std::move(flutter_args)]() { + // this ptr is valid because the shell is running. + + std::lock_guard lock(*bridge_mutex_); + auto node = bridge_->GetNodeById(id); + if (!node) { + return; + } + if (flutter_action == ACTIONS_::kCustomAction) { + bridge_->SendSemanticsEvent( + node, ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_INVALID, nullptr); + return; + } + if (action == ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_FORWARD || + action == ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_BACKWARD) { + node->performScrollAction = true; + } + if (action == ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SELECT_TEXT) { + node->performSelectAction = true; + } + + switch (action) { + case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLICK: + if (!node->HasAction(SemanticsAction::kTap)) { + platform_view_->SimulateTouchEvent(node); + break; + } + case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_LONG_CLICK: + case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_FORWARD: + case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_BACKWARD: + case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_COPY: + case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_PASTE: + case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CUT: + case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SET_TEXT: + case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SELECT_TEXT: + if (args.size()) { + platform_view_->DispatchSemanticsAction( + id, flutter_action, + fml::MallocMapping::Copy(args.data(), + args.size() * sizeof(uint8_t))); + } else { + platform_view_->DispatchSemanticsAction(id, flutter_action, {}); + } + break; + + case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_GAIN_ACCESSIBILITY_FOCUS: + bridge_->GainAccessibilityFocus(id); + break; + case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLEAR_ACCESSIBILITY_FOCUS: + bridge_->ClearAccessibilityFocus(id); + break; + + case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SET_CURSOR_POSITION: + // @todo flutter don't support this setting. + default: + break; + } + }); + + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_SUCCESSFUL; +} + +int32_t OHOSShellHolder::ClearAccessibilityFocus(int64_t elementId) { + std::lock_guard lock(*bridge_mutex_); + return bridge_->ClearAccessibilityFocus(elementId); +} + +int32_t OHOSShellHolder::GetAccessibilityNodeCursorPosition(int64_t elementId, + int32_t* index) { + std::lock_guard lock(*bridge_mutex_); + return bridge_->GetAccessibilityNodeCursorPosition(elementId, index); +} + +} // namespace flutter diff --git a/shell/platform/ohos/ohos_shell_holder.h b/shell/platform/ohos/ohos_shell_holder.h new file mode 100644 index 0000000000000000000000000000000000000000..4ae73d6e6d26f4411dd7f0df68d3ce84bff42f55 --- /dev/null +++ b/shell/platform/ohos/ohos_shell_holder.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SHELL_HOLDER_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SHELL_HOLDER_H_ +#define FML_USED_ON_EMBEDDER +#include "flutter/assets/asset_manager.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/unique_fd.h" +#include "flutter/lib/ui/window/viewport_metrics.h" +#include "flutter/runtime/platform_data.h" +#include "flutter/shell/common/run_configuration.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/common/thread_host.h" + +#include "accessibility/ohos_semantics_bridge.h" +#include "flutter/assets/asset_resolver.h" +#include "flutter/common/settings.h" +#include "flutter/shell/platform/ohos/napi/platform_view_ohos_napi.h" +#include "flutter/shell/platform/ohos/platform_view_ohos.h" + +#include "napi_common.h" +#include "ohos_asset_provider.h" +#include "ohos_image_generator.h" + +namespace flutter { + +class OHOSShellHolder { + public: + OHOSShellHolder(const flutter::Settings& settings, + std::shared_ptr napi_facade, + void* plateform_loop); + + ~OHOSShellHolder(); + bool IsValid() const; + + const flutter::Settings& GetSettings() const; + + fml::WeakPtr GetPlatformView(); + + void NotifyLowMemoryWarning(); + + void Launch(std::unique_ptr hap_asset_provider, + const std::string& entrypoint, + const std::string& libraryUrl, + const std::vector& entrypoint_args); + + std::unique_ptr Spawn( + std::shared_ptr napi_facade, + const std::string& entrypoint, + const std::string& libraryUrl, + const std::string& initial_route, + const std::vector& entrypoint_args) const; + + const std::shared_ptr& GetPlatformMessageHandler() + const { + return shell_->GetPlatformMessageHandler(); + } + + static void InitializeSystemFont(); + + void ReloadSystemFonts(); + + void SetAccessibilityProvider(ArkUI_AccessibilityProvider* provider); + + int32_t FindFocusNode(int32_t id, + ArkUI_AccessibilityFocusType focusType, + ArkUI_AccessibilityElementInfo* info); + int32_t FindNextFocusNode(int32_t id, + ArkUI_AccessibilityFocusMoveDirection direction, + ArkUI_AccessibilityElementInfo* info); + + int32_t FillNodesWithSearchText(int32_t id, + const char* text, + ArkUI_AccessibilityElementInfoList* list); + + int32_t FillNodesWithSearch(int32_t id, + ArkUI_AccessibilitySearchMode mode, + ArkUI_AccessibilityElementInfoList* list); + + int32_t ExecuteAction(int64_t elementId, + ArkUI_Accessibility_ActionType action, + ArkUI_AccessibilityActionArguments* actionArguments); + + int32_t ClearAccessibilityFocus(int64_t elementId); + + int32_t GetAccessibilityNodeCursorPosition(int64_t elementId, int32_t* index); + + private: + std::optional BuildRunConfiguration( + const std::string& entrypoint, + const std::string& libraryUrl, + const std::vector& entrypoint_args) const; + + const flutter::Settings settings_; + std::shared_ptr bridge_; + std::shared_ptr bridge_mutex_; + fml::WeakPtr platform_view_; + std::shared_ptr thread_host_; + std::unique_ptr shell_; + uint64_t next_pointer_flow_id_ = 0; + + std::unique_ptr asset_provider_; + + std::shared_ptr napi_facade_; + + OHOSShellHolder(const flutter::Settings& settings, + const std::shared_ptr& napi_facade, + const std::shared_ptr& thread_host, + std::unique_ptr shell, + std::unique_ptr hap_asset_provider, + const fml::WeakPtr& platform_view); + + static void ThreadDestructCallback(void* value); + + FML_DISALLOW_COPY_AND_ASSIGN(OHOSShellHolder); +}; +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SHELL_HOLDER_H_ diff --git a/shell/platform/ohos/ohos_surface_gl_impeller.cpp b/shell/platform/ohos/ohos_surface_gl_impeller.cpp new file mode 100755 index 0000000000000000000000000000000000000000..16425a53c1e550a47eae086950c634230d614c32 --- /dev/null +++ b/shell/platform/ohos/ohos_surface_gl_impeller.cpp @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "ohos_surface_gl_impeller.h" + +#include "flutter/fml/logging.h" +#include "flutter/impeller/entity/gles/entity_shaders_gles.h" +#include "flutter/impeller/renderer/backend/gles/context_gles.h" +#include "flutter/impeller/renderer/backend/gles/proc_table_gles.h" +#include "flutter/impeller/toolkit/egl/context.h" +#include "flutter/impeller/toolkit/egl/surface.h" +#include "flutter/shell/gpu/gpu_surface_gl_impeller.h" + +namespace flutter { + +class OHOSSurfaceGLImpeller::ReactorWorker final + : public impeller::ReactorGLES::Worker { + public: + ReactorWorker() = default; + + // |impeller::ReactorGLES::Worker| + ~ReactorWorker() override = default; + + // |impeller::ReactorGLES::Worker| + bool CanReactorReactOnCurrentThreadNow( + const impeller::ReactorGLES& reactor) const override { + impeller::ReaderLock lock(mutex_); + auto found = reactions_allowed_.find(std::this_thread::get_id()); + if (found == reactions_allowed_.end()) { + return false; + } + return found->second; + } + + void SetReactionsAllowedOnCurrentThread(bool allowed) { + impeller::WriterLock lock(mutex_); + reactions_allowed_[std::this_thread::get_id()] = allowed; + } + + private: + mutable impeller::RWMutex mutex_; + std::map reactions_allowed_ IPLR_GUARDED_BY(mutex_); + + FML_DISALLOW_COPY_AND_ASSIGN(ReactorWorker); +}; + +// for init use +static std::shared_ptr CreateImpellerContext( + const std::shared_ptr& worker, + bool enable_gpu_tracing) { + auto proc_table = std::make_unique( + impeller::egl::CreateProcAddressResolver()); + + if (!proc_table->IsValid()) { + FML_LOG(ERROR) << "Could not create OpenGL proc table."; + return nullptr; + } + + std::vector> shader_mappings = { + std::make_shared( + impeller_entity_shaders_gles_data, + impeller_entity_shaders_gles_length), + }; + + auto context = impeller::ContextGLES::Create( + std::move(proc_table), shader_mappings, enable_gpu_tracing); + if (!context) { + FML_LOG(ERROR) << "Could not create OpenGLES Impeller Context."; + return nullptr; + } + + if (!context->AddReactorWorker(worker).has_value()) { + FML_LOG(ERROR) << "Could not add reactor worker."; + return nullptr; + } + FML_LOG(ERROR) << "Using the Impeller rendering backend."; + + return context; +} + +OHOSSurfaceGLImpeller::OHOSSurfaceGLImpeller( + const std::shared_ptr& ohos_context, + bool enable_gpu_tracing) + : OHOSSurface(ohos_context), + reactor_worker_(std::make_shared()) { + auto display = std::make_unique(); + if (!display->IsValid()) { + FML_DLOG(ERROR) << "Could not create EGL display."; + return; + } + + impeller::egl::ConfigDescriptor desc; + desc.api = impeller::egl::API::kOpenGLES2; + desc.color_format = impeller::egl::ColorFormat::kRGBA8888; + desc.depth_bits = impeller::egl::DepthBits::kZero; + desc.stencil_bits = impeller::egl::StencilBits::kEight; + desc.samples = impeller::egl::Samples::kFour; + + desc.surface_type = impeller::egl::SurfaceType::kWindow; + auto onscreen_config = display->ChooseConfig(desc); + if (!onscreen_config) { + FML_DLOG(ERROR) << "Could not choose onscreen config."; + return; + } + + desc.surface_type = impeller::egl::SurfaceType::kPBuffer; + auto offscreen_config = display->ChooseConfig(desc); + if (!offscreen_config) { + FML_DLOG(ERROR) << "Could not choose offscreen config."; + return; + } + + auto onscreen_context = display->CreateContext(*onscreen_config, nullptr); + if (!onscreen_context) { + FML_DLOG(ERROR) << "Could not create onscreen context."; + return; + } + + auto offscreen_context = + display->CreateContext(*offscreen_config, onscreen_context.get()); + if (!offscreen_context) { + FML_DLOG(ERROR) << "Could not create offscreen context."; + return; + } + + auto offscreen_surface = + display->CreatePixelBufferSurface(*offscreen_config, 1u, 1u); + if (!offscreen_surface) { + FML_DLOG(ERROR) << "Could not create offscreen surface."; + return; + } + + if (!offscreen_context->MakeCurrent(*offscreen_surface)) { + FML_DLOG(ERROR) << "Could not make offscreen context current."; + return; + } + + auto impeller_context = + CreateImpellerContext(reactor_worker_, enable_gpu_tracing); + + if (!impeller_context) { + FML_DLOG(ERROR) << "Could not create Impeller context."; + return; + } + + if (!offscreen_context->ClearCurrent()) { + FML_DLOG(ERROR) << "Could not clear offscreen context."; + return; + } + + // Setup context listeners. + impeller::egl::Context::LifecycleListener listener = + [worker = + reactor_worker_](impeller::egl ::Context::LifecycleEvent event) { + switch (event) { + case impeller::egl::Context::LifecycleEvent::kDidMakeCurrent: + worker->SetReactionsAllowedOnCurrentThread(true); + break; + case impeller::egl::Context::LifecycleEvent::kWillClearCurrent: + worker->SetReactionsAllowedOnCurrentThread(false); + break; + } + }; + if (!onscreen_context->AddLifecycleListener(listener).has_value() || + !offscreen_context->AddLifecycleListener(listener).has_value()) { + FML_DLOG(ERROR) << "Could not add lifecycle listeners"; + } + + display_ = std::move(display); + onscreen_config_ = std::move(onscreen_config); + offscreen_config_ = std::move(offscreen_config); + offscreen_surface_ = std::move(offscreen_surface); + onscreen_context_ = std::move(onscreen_context); + offscreen_context_ = std::move(offscreen_context); + impeller_context_ = std::move(impeller_context); + + // The onscreen surface will be acquired once the native window is set. + + is_valid_ = true; +} + +OHOSSurfaceGLImpeller::~OHOSSurfaceGLImpeller() = default; + +// OHOSSurface +bool OHOSSurfaceGLImpeller::IsValid() const { + return is_valid_; +} + +// OHOSSurface +std::unique_ptr OHOSSurfaceGLImpeller::CreateGPUSurface( + GrDirectContext* gr_context) { + auto surface = + std::make_unique(this, // delegate + impeller_context_, // context + true // bool render_to_surface + ); + if (!surface->IsValid()) { + return nullptr; + } + return surface; +} + +// OHOSSurface +void OHOSSurfaceGLImpeller::TeardownOnScreenContext() { + GLContextClearCurrent(); + onscreen_surface_.reset(); +} + +// OHOSSurface +bool OHOSSurfaceGLImpeller::OnScreenSurfaceResize(const SkISize& size) { + // unused function + return RecreateOnscreenSurfaceAndMakeOnscreenContextCurrent(); +} + +// OHOSSurface +bool OHOSSurfaceGLImpeller::ResourceContextMakeCurrent() { + if (!offscreen_context_ || !offscreen_surface_) { + return false; + } + + return offscreen_context_->MakeCurrent(*offscreen_surface_); +} + +// OHOSSurface +bool OHOSSurfaceGLImpeller::ResourceContextClearCurrent() { + if (!offscreen_context_ || !offscreen_surface_) { + return false; + } + + return offscreen_context_->ClearCurrent(); +} + +// OHOSSurface +bool OHOSSurfaceGLImpeller::SetNativeWindow( + fml::RefPtr window) { + native_window_ = std::move(window); + return RecreateOnscreenSurfaceAndMakeOnscreenContextCurrent(); +} + +// OHOSSurface +std::unique_ptr OHOSSurfaceGLImpeller::CreateSnapshotSurface() { + // unreachable func + FML_UNREACHABLE(); + + return nullptr; +} + +// OHOSSurface +std::shared_ptr OHOSSurfaceGLImpeller::GetImpellerContext() { + return impeller_context_; +} + +// |GPUSurfaceGLDelegate| +std::unique_ptr OHOSSurfaceGLImpeller::GLContextMakeCurrent() { + return std::make_unique(OnGLContextMakeCurrent()); +} + +// |GPUSurfaceGLDelegate| +bool OHOSSurfaceGLImpeller::GLContextClearCurrent() { + if (!onscreen_surface_ || !onscreen_context_) { + return false; + } + + return onscreen_context_->ClearCurrent(); +} + +// |GPUSurfaceGLDelegate| +SurfaceFrame::FramebufferInfo OHOSSurfaceGLImpeller::GLContextFramebufferInfo() + const { + auto info = SurfaceFrame::FramebufferInfo{}; + info.supports_readback = true; + info.supports_partial_repaint = false; + return info; +} + +// |GPUSurfaceGLDelegate| +void OHOSSurfaceGLImpeller::GLContextSetDamageRegion( + const std::optional& region) { + // 不支持 +} + +// |GPUSurfaceGLDelegate| +bool OHOSSurfaceGLImpeller::GLContextPresent( + const GLPresentInfo& present_info) { + if (!onscreen_surface_) { + return false; + } + + return onscreen_surface_->Present(); +} + +// |GPUSurfaceGLDelegate| +GLFBOInfo OHOSSurfaceGLImpeller::GLContextFBO(GLFrameInfo frame_info) const { + // FBO0 is the default window bound framebuffer in EGL environments. + return GLFBOInfo{ + .fbo_id = 0, + }; +} + +// |GPUSurfaceGLDelegate| +sk_sp OHOSSurfaceGLImpeller::GetGLInterface() const { + return nullptr; +} + +bool OHOSSurfaceGLImpeller::OnGLContextMakeCurrent() { + if (!onscreen_surface_ || !onscreen_context_) { + return false; + } + + return onscreen_context_->MakeCurrent(*onscreen_surface_); +} + +bool OHOSSurfaceGLImpeller:: + RecreateOnscreenSurfaceAndMakeOnscreenContextCurrent() { + GLContextClearCurrent(); + if (!native_window_) { + return false; + } + onscreen_surface_.reset(); + auto onscreen_surface = display_->CreateWindowSurface( + *onscreen_config_, (EGLNativeWindowType)native_window_->Gethandle()); + if (!onscreen_surface) { + FML_DLOG(ERROR) << "Could not create onscreen surface."; + return false; + } + onscreen_surface_ = std::move(onscreen_surface); + return OnGLContextMakeCurrent(); +} + +} // namespace flutter diff --git a/shell/platform/ohos/ohos_surface_gl_impeller.h b/shell/platform/ohos/ohos_surface_gl_impeller.h new file mode 100755 index 0000000000000000000000000000000000000000..d804dd452a6a8cd1fc45183c00b12e0976e334e6 --- /dev/null +++ b/shell/platform/ohos/ohos_surface_gl_impeller.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SURFACE_GL_IMPELLER_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SURFACE_GL_IMPELLER_H_ + +#include "flutter/fml/macros.h" +#include "flutter/impeller/renderer/context.h" +#include "flutter/impeller/toolkit/egl/display.h" +#include "flutter/shell/gpu/gpu_surface_gl_delegate.h" +#include "surface/ohos_native_window.h" +#include "surface/ohos_surface.h" + +namespace flutter { + +class OHOSSurfaceGLImpeller final : public GPUSurfaceGLDelegate, + public OHOSSurface { + public: + OHOSSurfaceGLImpeller(const std::shared_ptr& ohos_context, + bool enable_gpu_tracing); + + ~OHOSSurfaceGLImpeller() override; + + // OHOSSurface + bool IsValid() const override; + + // OHOSSurface + std::unique_ptr CreateGPUSurface( + GrDirectContext* gr_context) override; + + // OHOSSurface + void TeardownOnScreenContext() override; + + // OHOSSurface + bool OnScreenSurfaceResize(const SkISize& size) override; + + // OHOSSurface + bool ResourceContextMakeCurrent() override; + + // OHOSSurface + bool ResourceContextClearCurrent() override; + + // OHOSSurface + bool SetNativeWindow(fml::RefPtr window) override; + + // OHOSSurface + std::unique_ptr CreateSnapshotSurface() override; + + // OHOSSurface + std::shared_ptr GetImpellerContext() override; + + // |GPUSurfaceGLDelegate| + std::unique_ptr GLContextMakeCurrent() override; + + // |GPUSurfaceGLDelegate| + bool GLContextClearCurrent() override; + + // |GPUSurfaceGLDelegate| + SurfaceFrame::FramebufferInfo GLContextFramebufferInfo() const override; + + // |GPUSurfaceGLDelegate| + void GLContextSetDamageRegion(const std::optional& region) override; + + // |GPUSurfaceGLDelegate| + bool GLContextPresent(const GLPresentInfo& present_info) override; + + // |GPUSurfaceGLDelegate| + GLFBOInfo GLContextFBO(GLFrameInfo frame_info) const override; + + // |GPUSurfaceGLDelegate| + sk_sp GetGLInterface() const override; + + private: + class ReactorWorker; + std::shared_ptr reactor_worker_; + std::unique_ptr display_; + std::unique_ptr onscreen_config_; + std::unique_ptr offscreen_config_; + std::unique_ptr onscreen_surface_; + std::unique_ptr offscreen_surface_; + std::unique_ptr onscreen_context_; + std::unique_ptr offscreen_context_; + std::shared_ptr impeller_context_; + fml::RefPtr native_window_; + + bool is_valid_ = false; + + bool OnGLContextMakeCurrent(); + + bool RecreateOnscreenSurfaceAndMakeOnscreenContextCurrent(); + + FML_DISALLOW_COPY_AND_ASSIGN(OHOSSurfaceGLImpeller); +}; + +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SURFACE_GL_IMPELLER_H_ \ No newline at end of file diff --git a/shell/platform/ohos/ohos_surface_gl_skia.cpp b/shell/platform/ohos/ohos_surface_gl_skia.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa23d4a06527ec66ec53455a0a3a8ca90284c02a --- /dev/null +++ b/shell/platform/ohos/ohos_surface_gl_skia.cpp @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/ohos_surface_gl_skia.h" + +#include +#include +#include + +#include "flutter/fml/logging.h" +#include "flutter/fml/memory/ref_ptr.h" +#include "flutter/shell/platform/ohos/ohos_egl_surface.h" +#include "flutter/shell/platform/ohos/ohos_shell_holder.h" + +namespace flutter { + +namespace { +// GL renderer string prefix used by the Ohos emulator GLES implementation. +constexpr char kEmulatorRendererPrefix[] = "Mali-G78"; +} // anonymous namespace + +OhosSurfaceGLSkia::OhosSurfaceGLSkia( + const std::shared_ptr& ohos_context) + : OHOSSurface(ohos_context), + onscreen_surface_(nullptr), + offscreen_surface_(nullptr) { + // Acquire the offscreen surface. + FML_LOG(INFO) << "OhosSurfaceGLSkia constructor"; + offscreen_surface_ = GLContextPtr()->CreateOffscreenSurface(); + if (!offscreen_surface_->IsValid()) { + FML_LOG(ERROR) << "OhosSurfaceGLSkia offscreen_surface_->IsValid() FAIL"; + offscreen_surface_ = nullptr; + } + FML_LOG(INFO) << "OhosSurfaceGLSkia constructor end"; +} + +OhosSurfaceGLSkia::~OhosSurfaceGLSkia() {} + +void OhosSurfaceGLSkia::TeardownOnScreenContext() { + // When the onscreen surface is destroyed, the context and the surface + // instance should be deleted. Issue: + // https://github.com/flutter/flutter/issues/64414 + GLContextPtr()->ClearCurrent(); + onscreen_surface_ = nullptr; +} + +bool OhosSurfaceGLSkia::IsValid() const { + return offscreen_surface_ && GLContextPtr()->IsValid(); +} + +std::unique_ptr OhosSurfaceGLSkia::CreateGPUSurface( + GrDirectContext* gr_context) { + if (gr_context) { + FML_LOG(INFO) << "gr_context NO null"; + return std::make_unique(sk_ref_sp(gr_context), this, + true); + } else { + sk_sp main_skia_context = + GLContextPtr()->GetMainSkiaContext(); + if (!main_skia_context) { + main_skia_context = GPUSurfaceGLSkia::MakeGLContext(this); + if (main_skia_context == nullptr) { + FML_LOG(ERROR) << "Could not make main_skia_context "; + } + GLContextPtr()->SetMainSkiaContext(main_skia_context); + } + return std::make_unique(main_skia_context, this, true); + } +} + +bool OhosSurfaceGLSkia::OnScreenSurfaceResize(const SkISize& size) { + FML_DCHECK(IsValid()); + FML_DCHECK(onscreen_surface_); + FML_DCHECK(native_window_); + + FML_LOG(INFO) << "OnScreenSurfaceResize update window size:" << size.width() + << "*" << size.height(); + if (onscreen_surface_ && onscreen_surface_->IsValid() && + size == onscreen_surface_->GetSize()) { + return true; + } + + // In EGL we need create the surface again. + SetNativeWindow(native_window_); + return true; +} + +bool OhosSurfaceGLSkia::ResourceContextMakeCurrent() { + FML_DCHECK(IsValid()); + auto status = offscreen_surface_->MakeCurrent(); + return status != OhosEGLSurfaceMakeCurrentStatus::kFailure; +} + +bool OhosSurfaceGLSkia::ResourceContextClearCurrent() { + FML_DCHECK(IsValid()); + EGLBoolean result = eglMakeCurrent(eglGetCurrentDisplay(), EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); + FML_LOG(ERROR) << "ResourceContextClearCurrent ~OhosSurfaceGLSkia"; + return result == EGL_TRUE; +} + +bool OhosSurfaceGLSkia::SetNativeWindow(fml::RefPtr window) { + FML_DCHECK(IsValid()); + if (!window) { + GLContextClearCurrent(); + native_window_ = nullptr; + onscreen_surface_ = nullptr; + return false; + } + + bool need_current = false; + if (onscreen_surface_ && onscreen_surface_->IsValid() && + onscreen_surface_->IsContextCurrent()) { + GLContextClearCurrent(); + need_current = true; + } + native_window_ = window; + // Ensure the destructor is called since it destroys the `EGLSurface` before + // creating a new onscreen surface. + onscreen_surface_ = nullptr; + // Create the onscreen surface. + FML_LOG(INFO) << "SetNativeWindow create onscreensurface"; + onscreen_surface_ = GLContextPtr()->CreateOnscreenSurface(window); + if (!onscreen_surface_->IsValid()) { + return false; + } + if (need_current) { + GLContextMakeCurrent(); + } + return true; +} + +bool OhosSurfaceGLSkia::PaintOffscreenData(OHNativeWindowBuffer* buffer, + int fence_fd) { + if (!native_window_ || !native_window_->IsValid() || buffer == nullptr) { + return false; + } + OHNativeWindow* onscreen_nativewindow = native_window_->Gethandle(); + int ret = + OH_NativeWindow_NativeWindowAttachBuffer(onscreen_nativewindow, buffer); + if (ret != 0) { + FML_LOG(ERROR) << "ohos_surface cannot attach onscreen nativewindow " + "buffer to window: ret error:" + << ret; + return false; + } + + ret = OH_NativeWindow_NativeWindowFlushBuffer(onscreen_nativewindow, buffer, + fence_fd, {}); + if (ret != 0) { + FML_LOG(INFO) << "ohos_surface flush last nativewindow buffer result: " + << ret; + } + FML_LOG(INFO) << "PaintOffscreenData " << buffer; + + OH_NativeWindow_DestroyNativeWindowBuffer(buffer); + return true; +}; + +std::unique_ptr OhosSurfaceGLSkia::GLContextMakeCurrent() { + FML_DCHECK(IsValid()); + FML_DCHECK(onscreen_surface_); + auto status = onscreen_surface_->MakeCurrent(); + auto default_context_result = std::make_unique( + status != OhosEGLSurfaceMakeCurrentStatus::kFailure); + return std::move(default_context_result); +} + +bool OhosSurfaceGLSkia::GLContextClearCurrent() { + FML_DCHECK(IsValid()); + return GLContextPtr()->ClearCurrent(); +} + +SurfaceFrame::FramebufferInfo OhosSurfaceGLSkia::GLContextFramebufferInfo() + const { + FML_DCHECK(IsValid()); + SurfaceFrame::FramebufferInfo res; + res.supports_readback = true; + res.supports_partial_repaint = onscreen_surface_->SupportsPartialRepaint(); + res.existing_damage = onscreen_surface_->InitialDamage(); + // Some devices (Pixel2 XL) needs EGL_KHR_partial_update rect aligned to 4, + // otherwise there are glitches + // (https://github.com/flutter/flutter/issues/97482#) + // Larger alignment might also be beneficial for tile base renderers. + res.horizontal_clip_alignment = 32; + res.vertical_clip_alignment = 32; + + return res; +} + +void OhosSurfaceGLSkia::GLContextSetDamageRegion( + const std::optional& region) { + FML_DCHECK(IsValid()); + onscreen_surface_->SetDamageRegion(region); +} + +bool OhosSurfaceGLSkia::GLContextPresent(const GLPresentInfo& present_info) { + FML_DCHECK(IsValid()); + FML_DCHECK(onscreen_surface_); + if (native_window_ && native_window_->IsValid() && + present_info.presentation_time) { + onscreen_surface_->SetPresentationTime(*present_info.presentation_time); + uint64_t present_time = + present_info.presentation_time->ToEpochDelta().ToNanoseconds(); + OH_NativeWindow_NativeWindowHandleOpt( + (OHNativeWindow*)native_window_->Gethandle(), + SET_DESIRED_PRESENT_TIMESTAMP, present_time); + } + return onscreen_surface_->SwapBuffers(present_info.frame_damage); +} + +GLFBOInfo OhosSurfaceGLSkia::GLContextFBO(GLFrameInfo frame_info) const { + FML_DCHECK(IsValid()); + // The default window bound framebuffer on Ohos. + return GLFBOInfo{ + .fbo_id = 0, + .existing_damage = onscreen_surface_->InitialDamage(), + }; +} + +// |GPUSurfaceGLDelegate| +sk_sp OhosSurfaceGLSkia::GetGLInterface() const { + // This is a workaround for a bug in the Ohos emulator EGL/GLES + // implementation. Some versions of the emulator will not update the + // GL version string when the process switches to a new EGL context + // unless the EGL context is being made current for the first time. + // The inaccurate version string will be rejected by Skia when it + // tries to build the GrGLInterface. Flutter can work around this + // by creating a new context, making it current to force an update + // of the version, and then reverting to the previous context. + const char* gl_renderer = + reinterpret_cast(glGetString(GL_RENDERER)); + + FML_DLOG(INFO) << "OhosSurfaceGLSkia::GetGLInterface 1"; + if (gl_renderer && strncmp(gl_renderer, kEmulatorRendererPrefix, + strlen(kEmulatorRendererPrefix)) == 0) { + FML_DLOG(INFO) << "OhosSurfaceGLSkia::GetGLInterface 2"; + EGLContext new_context = GLContextPtr()->CreateNewContext(); + if (new_context != EGL_NO_CONTEXT) { + FML_DLOG(INFO) << "OhosSurfaceGLSkia::GetGLInterface 3"; + EGLContext old_context = eglGetCurrentContext(); + EGLDisplay display = eglGetCurrentDisplay(); + EGLSurface draw_surface = eglGetCurrentSurface(EGL_DRAW); + EGLSurface read_surface = eglGetCurrentSurface(EGL_READ); + [[maybe_unused]] EGLBoolean result = + eglMakeCurrent(display, draw_surface, read_surface, new_context); + FML_DCHECK(result == EGL_TRUE); + result = eglMakeCurrent(display, draw_surface, read_surface, old_context); + FML_DCHECK(result == EGL_TRUE); + result = eglDestroyContext(display, new_context); + FML_DCHECK(result == EGL_TRUE); + } + } + + FML_DLOG(INFO) << "OhosSurfaceGLSkia::GetGLInterface 4"; + return GPUSurfaceGLDelegate::GetGLInterface(); +} + +OhosContextGLSkia* OhosSurfaceGLSkia::GLContextPtr() const { + return reinterpret_cast(ohos_context_.get()); +} + +std::unique_ptr OhosSurfaceGLSkia::CreateSnapshotSurface() { + FML_DLOG(INFO) << "CreateSnapshotSurface "; + if (!onscreen_surface_ || !onscreen_surface_->IsValid()) { + onscreen_surface_ = GLContextPtr()->CreatePbufferSurface(); + } + sk_sp main_skia_context = + GLContextPtr()->GetMainSkiaContext(); + if (!main_skia_context) { + main_skia_context = GPUSurfaceGLSkia::MakeGLContext(this); + FML_DLOG(INFO) << "CreateSnapshotSurface create and make skia context " + << main_skia_context; + GLContextPtr()->SetMainSkiaContext(main_skia_context); + } + + return std::make_unique(main_skia_context, this, true); +} + +} // namespace flutter diff --git a/shell/platform/ohos/ohos_surface_gl_skia.h b/shell/platform/ohos/ohos_surface_gl_skia.h new file mode 100755 index 0000000000000000000000000000000000000000..8cd2076b341b02c91031ba534832d1373ceeef77 --- /dev/null +++ b/shell/platform/ohos/ohos_surface_gl_skia.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SURFACE_GL_SKIA_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SURFACE_GL_SKIA_H_ + +#include + +#include "flutter/fml/macros.h" +#include "flutter/shell/gpu/gpu_surface_gl_skia.h" +#include "flutter/shell/platform/ohos/napi/platform_view_ohos_napi.h" +#include "flutter/shell/platform/ohos/ohos_context_gl_skia.h" +#include "flutter/shell/platform/ohos/ohos_environment_gl.h" +#include "flutter/shell/platform/ohos/surface/ohos_surface.h" + +namespace flutter { + +class OhosSurfaceGLSkia final : public GPUSurfaceGLDelegate, + public OHOSSurface { + public: + OhosSurfaceGLSkia(const std::shared_ptr& ohos_context); + + ~OhosSurfaceGLSkia() override; + + // |OhosSurface| + bool IsValid() const override; + + // |OhosSurface| + std::unique_ptr CreateGPUSurface( + GrDirectContext* gr_context) override; + + // |OhosSurface| + void TeardownOnScreenContext() override; + + // |OhosSurface| + bool OnScreenSurfaceResize(const SkISize& size) override; + + // |OhosSurface| + bool ResourceContextMakeCurrent() override; + + // |OhosSurface| + bool ResourceContextClearCurrent() override; + + // |OhosSurface| + bool SetNativeWindow(fml::RefPtr window) override; + + // |OhosSurface| + virtual std::unique_ptr CreateSnapshotSurface() override; + + // |GPUSurfaceGLDelegate| + std::unique_ptr GLContextMakeCurrent() override; + + // |GPUSurfaceGLDelegate| + bool GLContextClearCurrent() override; + + // |GPUSurfaceGLDelegate| + SurfaceFrame::FramebufferInfo GLContextFramebufferInfo() const override; + + // |GPUSurfaceGLDelegate| + void GLContextSetDamageRegion(const std::optional& region) override; + + // |GPUSurfaceGLDelegate| + bool GLContextPresent(const GLPresentInfo& present_info) override; + + // |GPUSurfaceGLDelegate| + GLFBOInfo GLContextFBO(GLFrameInfo frame_info) const override; + + // |GPUSurfaceGLDelegate| + sk_sp GetGLInterface() const override; + + // Obtain a raw pointer to the on-screen OhosEGLSurface. + // + // This method is intended for use in tests. Callers must not + // delete the returned pointer. + OhosEGLSurface* GetOnscreenSurface() const { return onscreen_surface_.get(); } + + bool PaintOffscreenData(OHNativeWindowBuffer* buffer, int fence_fd) override; + + private: + std::unique_ptr onscreen_surface_; + std::unique_ptr offscreen_surface_; + + //---------------------------------------------------------------------------- + /// @brief Takes the super class OhosSurface's OhosContext and + /// return a raw pointer to an OhosContextGL. + /// + OhosContextGLSkia* GLContextPtr() const; + + FML_DISALLOW_COPY_AND_ASSIGN(OhosSurfaceGLSkia); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SURFACE_GL_SKIA_H_ diff --git a/shell/platform/ohos/ohos_surface_software.cpp b/shell/platform/ohos/ohos_surface_software.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b76c3a7594a5b1c6cfa4c85b0ad3930419a1f23 --- /dev/null +++ b/shell/platform/ohos/ohos_surface_software.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/ohos_surface_software.h" +#include +#include +#include "napi_common.h" +#include "ohos_logging.h" +#include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "types.h" + +namespace flutter { + +bool GetSkColorType(int32_t buffer_format, + SkColorType* color_type, + SkAlphaType* alpha_type) { + switch (buffer_format) { + case kPixelFmtRgba8888: // kPixelFmtRgba8888 + *color_type = kRGBA_8888_SkColorType; + *alpha_type = kPremul_SkAlphaType; + return true; + default: + return false; + } +} + +OHOSSurfaceSoftware::OHOSSurfaceSoftware( + const std::shared_ptr& ohos_context) + : OHOSSurface(ohos_context) { + GetSkColorType(12, &target_color_type_, &target_alpha_type_); +} + +OHOSSurfaceSoftware::~OHOSSurfaceSoftware() {} + +bool OHOSSurfaceSoftware::IsValid() const { + return true; +} + +// |OHOSSurface| +bool OHOSSurfaceSoftware::ResourceContextMakeCurrent() { + return false; +} + +// |OHOSSurface| +bool OHOSSurfaceSoftware::ResourceContextClearCurrent() { + return false; +} + +// |OHOSSurface| +std::unique_ptr OHOSSurfaceSoftware::CreateGPUSurface( + GrDirectContext* gr_context) { + LOGD("CreateGPUSurface start"); + if (!IsValid()) { + return nullptr; + } + + FML_DLOG(INFO) << "CreateGPUSurface"; + + auto surface = + std::make_unique(this, true /* render to surface */); + + if (!surface->IsValid()) { + FML_DLOG(INFO) << "CreateGPUSurface failed."; + return nullptr; + } + LOGD("CreateGPUSurface end"); + return surface; +} + +// |OHOSSurface| +void OHOSSurfaceSoftware::TeardownOnScreenContext() { + FML_DLOG(INFO) << "TeardownOnScreenContext"; +} + +// |OHOSSurface| +bool OHOSSurfaceSoftware::OnScreenSurfaceResize(const SkISize& size) { + FML_DLOG(INFO) << "OnScreenSurfaceResize"; + return true; +} + +// |OHOSSurface| +bool OHOSSurfaceSoftware::SetNativeWindow( + fml::RefPtr window) { + FML_DLOG(INFO) << "SetNativeWindow"; + native_window_ = std::move(window); + if (!(native_window_ && native_window_->IsValid())) { + FML_DLOG(INFO) << "SetNativeWindow failed."; + return false; + } + + LOGD("SetNativeWindow true"); + return true; +} + +// |GPUSurfaceSoftwareDelegate| +sk_sp OHOSSurfaceSoftware::AcquireBackingStore(const SkISize& size) { + FML_DLOG(INFO) << "AcquireBackingStore..."; + if (!IsValid()) { + LOGE("AcquireBackingStore the surface is Invalid"); + return nullptr; + } + + if (sk_surface_ != nullptr && + SkISize::Make(sk_surface_->width(), sk_surface_->height()) == size) { + // The old and new surface sizes are the same. Nothing to do here. + return sk_surface_; + } + + LOGE("SkImageInfofWidth=%{public}d, fHeight=%{public}d", size.fWidth, + size.fHeight); + SkImageInfo image_info = + SkImageInfo::Make(size.fWidth, size.fHeight, target_color_type_, + target_alpha_type_, SkColorSpace::MakeSRGB()); + + FML_DLOG(INFO) << "AcquireBackingStore...MakeRaster "; + sk_surface_ = SkSurfaces::Raster(image_info); + + LOGD("AcquireBackingStore end"); + return sk_surface_; +} + +// |GPUSurfaceSoftwareDelegate| +/*将 backing_store 画布中的数据 绘制到 native_window 构筑的画布中 */ +bool OHOSSurfaceSoftware::PresentBackingStore(sk_sp backing_store) { + FML_DLOG(INFO) << "PresentBackingStore...MakeRaster "; + if (!IsValid() || backing_store == nullptr) { + LOGE("PresentBackingStore backing_store is inValid"); + return false; + } + + SkPixmap pixmap; + LOGE("PresentBackingStore peekPixels ...."); + + if (!backing_store->peekPixels(&pixmap)) { + LOGE("PresentBackingStore peekPixels failed"); + return false; + } + + OHNativeWindowBuffer* buffer = nullptr; + int fenceFd = -1; + FML_DLOG(INFO) << "PresentBackingStore Requestbuffer ..." + << (int64_t)native_window_.get()->Gethandle(); + if (native_window_.get() == nullptr || !native_window_.get()->IsValid()) { + FML_DLOG(ERROR) + << "PresentBackingStore Requestbuffer ...native_window is invalid " + << (int64_t)native_window_.get()->Gethandle(); + return false; + } + + int32_t ret = OH_NativeWindow_NativeWindowRequestBuffer( + native_window_.get()->Gethandle(), &buffer, &fenceFd); + if (ret != 0) { + LOGE( + "PresentBackingStore OH_NativeWindow_NativeWindowRequestBuffer failed " + ":%{public}d", + ret); + return false; + } + + BufferHandle* bufferHandle = + OH_NativeWindow_GetBufferHandleFromNative(buffer); + + if (bufferHandle == nullptr) { + FML_DLOG(ERROR) << "PresentBackingStore " + "OH_NativeWindow_GetBufferHandleFromNative failed ."; + OH_NativeWindow_DestroyNativeWindowBuffer(buffer); + return false; + } + LOGI( + "BufferHandle.fd:%{public}d,w:%{public}d,h:%{public}d,stride:%{public}d," + "format:%{public}d,usage:%{public}ld,virAddr:%{public}p,phyAddr:%{public}" + "ld,key:%{public}d", + bufferHandle->fd, bufferHandle->width, bufferHandle->height, + bufferHandle->stride, bufferHandle->format, bufferHandle->usage, + bufferHandle->virAddr, bufferHandle->phyAddr, bufferHandle->key); + void* virAddr = mmap(nullptr, bufferHandle->size, PROT_READ | PROT_WRITE, + MAP_SHARED, bufferHandle->fd, 0); + if (virAddr == MAP_FAILED) { + FML_DLOG(ERROR) << "mmap BufferHandle.virAddr failed "; + OH_NativeWindow_DestroyNativeWindowBuffer(buffer); + return false; + } + + { + SkColorType color_type; + SkAlphaType alpha_type; + FML_DLOG(INFO) << "GetSkColorType..."; + if (GetSkColorType(bufferHandle->format, &color_type, &alpha_type)) { + SkImageInfo native_image_info = SkImageInfo::Make( + bufferHandle->width, bufferHandle->height, color_type, alpha_type); + FML_DLOG(INFO) << "native_image_info.w:" << native_image_info.width() + << ",h:" << native_image_info.height(); + int bytesPerPixel = 1; // SkColorTypeBytesPerPixel(color_type); + FML_DLOG(INFO) << "MakeRasterDirect,bytesPerPixel:" << bytesPerPixel; + + std::unique_ptr canvas = SkCanvas::MakeRasterDirect( + native_image_info, virAddr, bufferHandle->stride * bytesPerPixel); + FML_DLOG(INFO) << "MakeRasterDirect,created canvas:" + << (int64_t)canvas.get(); + + if (canvas) { + SkBitmap bitmap; + if (bitmap.installPixels(pixmap)) { + FML_DLOG(INFO) << "MakeRasterDirect,canvasdrawImageRect.width:" + << bufferHandle->width << ",height" + << bufferHandle->height; + canvas->drawImageRect( + bitmap.asImage(), + SkRect::MakeIWH(bufferHandle->width, bufferHandle->height), + SkSamplingOptions()); + + } else { + FML_DLOG(INFO) << "bitmap.installPixels failed ."; + } + + } else { + FML_DLOG(INFO) << "MakeRasterDirect Failed."; + } + } else { + FML_DLOG(INFO) << "GetSkColorType Failed."; + } + } + + Region region{nullptr, 0}; + if (virAddr != nullptr) { + munmap(virAddr, bufferHandle->size); + } + LOGI("OH_NativeWindow_NativeWindowFlushBuffer ...."); + ret = OH_NativeWindow_NativeWindowFlushBuffer( + native_window_.get()->Gethandle(), buffer, fenceFd, region); + LOGI("PresentBackingStore flush Buffer :%{public}d", ret); + OH_NativeWindow_DestroyNativeWindowBuffer(buffer); + return ret == 0; +} + +} // namespace flutter diff --git a/shell/platform/ohos/ohos_surface_software.h b/shell/platform/ohos/ohos_surface_software.h new file mode 100644 index 0000000000000000000000000000000000000000..10a3c8ec95e0073a5b764a987ef614ba95cb9c54 --- /dev/null +++ b/shell/platform/ohos/ohos_surface_software.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SURFACE_SOFTWARE_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SURFACE_SOFTWARE_H_ + +#include "flutter/fml/macros.h" +#include "flutter/shell/gpu/gpu_surface_software.h" +#include "flutter/shell/platform/ohos/surface/ohos_surface.h" + +#include "flutter/shell/platform/ohos/surface/ohos_native_window.h" + +namespace flutter { + +class OHOSSurfaceSoftware final : public OHOSSurface, + public GPUSurfaceSoftwareDelegate { + public: + OHOSSurfaceSoftware(const std::shared_ptr& ohos_context); + ~OHOSSurfaceSoftware() override; + + bool IsValid() const override; + + // |OHOSSurface| + bool ResourceContextMakeCurrent() override; + + // |OHOSSurface| + bool ResourceContextClearCurrent() override; + + // |OHOSSurface| + std::unique_ptr CreateGPUSurface( + GrDirectContext* gr_context) override; + + // |OHOSSurface| + void TeardownOnScreenContext() override; + + // |OHOSSurface| + bool OnScreenSurfaceResize(const SkISize& size) override; + + // |OHOSSurface| + bool SetNativeWindow(fml::RefPtr window) override; + + // |GPUSurfaceSoftwareDelegate| + sk_sp AcquireBackingStore(const SkISize& size) override; + + // |GPUSurfaceSoftwareDelegate| + bool PresentBackingStore(sk_sp backing_store) override; + + private: + sk_sp sk_surface_; + fml::RefPtr native_window_; + SkColorType target_color_type_; + SkAlphaType target_alpha_type_; + + FML_DISALLOW_COPY_AND_ASSIGN(OHOSSurfaceSoftware); +}; +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SURFACE_SOFTWARE_H_ \ No newline at end of file diff --git a/shell/platform/ohos/ohos_surface_vulkan_impeller.cpp b/shell/platform/ohos/ohos_surface_vulkan_impeller.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6c435ca7c86e2cfdda2b1e210f63172bbb88a518 --- /dev/null +++ b/shell/platform/ohos/ohos_surface_vulkan_impeller.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "ohos_surface_vulkan_impeller.h" + +#include +#include + +#include "flutter/fml/logging.h" +#include "flutter/fml/memory/ref_ptr.h" +#include "flutter/impeller/renderer/backend/vulkan/context_vk.h" +#include "flutter/shell/gpu/gpu_surface_vulkan_impeller.h" +#include "fml/trace_event.h" +#include "shell/platform/ohos/surface/ohos_surface.h" + +namespace flutter { + +OHOSSurfaceVulkanImpeller::OHOSSurfaceVulkanImpeller( + const std::shared_ptr& ohos_context) + : OHOSSurface(ohos_context) { + is_valid_ = ohos_context->IsValid(); + auto& context_vk = + impeller::ContextVK::Cast(*ohos_context->GetImpellerContext()); + surface_context_vk_ = context_vk.CreateSurfaceContext(); +} + +OHOSSurfaceVulkanImpeller::~OHOSSurfaceVulkanImpeller() {} + +// |OHOSSurface| +bool OHOSSurfaceVulkanImpeller::IsValid() const { + return is_valid_; +} + +// |OHOSSurface| +std::unique_ptr OHOSSurfaceVulkanImpeller::CreateGPUSurface( + GrDirectContext* gr_context) { + if (!IsValid()) { + return nullptr; + } + + std::lock_guard lock(surface_preload_mutex_); + + if (preload_gpu_surface_ && preload_gpu_surface_->IsValid()) { + preload_gpu_surface_->SetDelegate(this); + return std::move(preload_gpu_surface_); + } + + std::unique_ptr gpu_surface = + std::make_unique(surface_context_vk_); + + if (!gpu_surface->IsValid()) { + return nullptr; + } + gpu_surface->SetDelegate(this); + return gpu_surface; +} + +// |OHOSSurface| +void OHOSSurfaceVulkanImpeller::TeardownOnScreenContext() { + // We should do samething as OhosSurfaceGLSkia. + // If a new engine attaches while the previous detached engine is still not + // destroyed, it may cause a stall if the swapchain is not cleared. + surface_context_vk_->ClearSwapchain(); + native_window_ = nullptr; + is_surface_preload_ = false; +} + +// |OHOSSurface| +bool OHOSSurfaceVulkanImpeller::OnScreenSurfaceResize(const SkISize& size) { + surface_context_vk_->UpdateSurfaceSize( + impeller::ISize{size.width(), size.height()}); + return true; +} + +// |OHOSSurface| +bool OHOSSurfaceVulkanImpeller::ResourceContextMakeCurrent() { + // do nothing (it is not opengl) + return true; +} + +// |OHOSSurface| +bool OHOSSurfaceVulkanImpeller::ResourceContextClearCurrent() { + // do nothing (it is not opengl) + return true; +} + +// |OHOSSurface| +bool OHOSSurfaceVulkanImpeller::SetNativeWindow( + fml::RefPtr window) { + if (!window) { + native_window_ = nullptr; + return false; + } + TRACE_EVENT0("flutter", "OHOSSurfaceVulkanImpeller-SetNativeWindow"); + + native_window_ = std::move(window); + bool success = native_window_ && native_window_->IsValid(); + + if (success) { + auto surface = + surface_context_vk_->CreateOHOSSurface(native_window_->Gethandle()); + + if (!surface) { + FML_LOG(ERROR) << "Could not create a vulkan surface."; + return false; + } + auto size = native_window_->GetSize(); + return surface_context_vk_->SetWindowSurface( + std::move(surface), impeller::ISize{size.width(), size.height()}); + } + + native_window_ = nullptr; + return false; +} + +bool OHOSSurfaceVulkanImpeller::PrepareOffscreenWindow(int32_t width, + int32_t height) { + // Currently, when creating a Vulkan swapchain, a completely clean + // NativeWindow is required. Any flushBuffer actions on this window will cause + // the swapchain creation to stall (creation involves requesting all buffers + // within the Vulkan implementation, and flushing will occupy one buffer + // position, causing the buffer queue to fill up prematurely and preventing + // Vulkan from obtaining enough free buffers). Therefore, we cannot directly + // pass the off-screen rendered buffer to RS. Additionally, swapchain creation + // requires requesting all buffers, making it time-consuming and costly. + // Rendering the buffer to the screen buffer is also challenging: we need to + // obtain various contexts wrapped by Impeller and invoke them according to + // its conventions. Hence, we opt to only perform the time-consuming surface + // creation work here without using NativeImage for off-screen rendering. + TRACE_EVENT0("flutter", "impeller-PrepareContext"); + std::lock_guard lock(surface_preload_mutex_); + if (!preload_gpu_surface_ && !is_surface_preload_) { + is_surface_preload_ = true; + preload_gpu_surface_ = + std::make_unique(surface_context_vk_); + } + // return false means that it will not invoke PlatformView::NotifyCreated(). + // return false; + // If we want to skip time-consuming tasks during the first frame, we can + // render it to offscreen window. However, the result of the offscreen + // rendering will not be drawn to the onscreen window. + return OHOSSurface::PrepareOffscreenWindow(width, height); +} + +void OHOSSurfaceVulkanImpeller::PrepareGpuSurface() { + std::lock_guard lock(surface_preload_mutex_); + if (!preload_gpu_surface_ && !is_surface_preload_) { + is_surface_preload_ = true; + preload_gpu_surface_ = + std::make_unique(surface_context_vk_); + } +} + +std::shared_ptr +OHOSSurfaceVulkanImpeller::GetImpellerContext() { + return surface_context_vk_; +} + +bool OHOSSurfaceVulkanImpeller::SetPresentInfo( + const VulkanPresentInfo& present_info) { + if (native_window_ && native_window_->IsValid() && + present_info.presentation_time) { + uint64_t present_time = + present_info.presentation_time->ToEpochDelta().ToNanoseconds(); + OH_NativeWindow_NativeWindowHandleOpt( + (OHNativeWindow*)native_window_->Gethandle(), + SET_DESIRED_PRESENT_TIMESTAMP, present_time); + return true; + } + return false; +} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/ohos_surface_vulkan_impeller.h b/shell/platform/ohos/ohos_surface_vulkan_impeller.h new file mode 100644 index 0000000000000000000000000000000000000000..57625b0ad7ed6bdc92b7d6737725c2bf54ba58ce --- /dev/null +++ b/shell/platform/ohos/ohos_surface_vulkan_impeller.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SURFACE_VULKAN_IMPELLER_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SURFACE_VULKAN_IMPELLER_H_ + +#include +#include "flutter/fml/concurrent_message_loop.h" +#include "flutter/fml/macros.h" +#include "flutter/impeller/renderer/backend/vulkan/surface_context_vk.h" +#include "flutter/impeller/renderer/context.h" + +#include "shell/gpu/gpu_surface_vulkan_impeller.h" +#include "shell/platform/ohos/ohos_context_vulkan_impeller.h" +#include "surface/ohos_native_window.h" +#include "surface/ohos_surface.h" + +namespace flutter { + +class OHOSSurfaceVulkanImpeller : public GPUSurfaceVulkanDelegate, + public OHOSSurface { + public: + explicit OHOSSurfaceVulkanImpeller( + const std::shared_ptr& ohos_context); + + ~OHOSSurfaceVulkanImpeller() override; + + // |OHOSSurface| + bool IsValid() const override; + + // |OHOSSurface| + std::unique_ptr CreateGPUSurface( + GrDirectContext* gr_context) override; + + // |OHOSSurface| + void TeardownOnScreenContext() override; + + // |OHOSSurface| + bool OnScreenSurfaceResize(const SkISize& size) override; + + // |OHOSSurface| + bool ResourceContextMakeCurrent() override; + + // |OHOSSurface| + bool ResourceContextClearCurrent() override; + + // |OHOSSurface| + std::shared_ptr GetImpellerContext() override; + + // |OHOSSurface| + bool SetNativeWindow(fml::RefPtr window) override; + + bool PrepareOffscreenWindow(int32_t width, int32_t height) override; + + void PrepareGpuSurface() override; + + // |GPUSurfaceVulkanDelegate| + bool SetPresentInfo(const VulkanPresentInfo& present_info) override; + + // |GPUSurfaceVulkanDelegate| + const vulkan::VulkanProcTable& vk() override { + // will never be invoke + return vk_; + }; + + // |GPUSurfaceVulkanDelegate| + FlutterVulkanImage AcquireImage(const SkISize& size) override { + // will never be invoke + return FlutterVulkanImage(); + }; + + // |GPUSurfaceVulkanDelegate| + bool PresentImage(VkImage image, VkFormat format) override { + // will never be invoke + return false; + }; + + private: + std::shared_ptr surface_context_vk_; + std::unique_ptr preload_gpu_surface_; + std::mutex surface_preload_mutex_; + bool is_valid_ = false; + bool is_surface_preload_ = false; + + // will never be used + vulkan::VulkanProcTable vk_; + FML_DISALLOW_COPY_AND_ASSIGN(OHOSSurfaceVulkanImpeller); +}; +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_SURFACE_VULKAN_IMPELLER_H_ \ No newline at end of file diff --git a/shell/platform/ohos/ohos_touch_processor.cpp b/shell/platform/ohos/ohos_touch_processor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..444f5847be408fc4c9aaac81d69f5a3a40f79bf8 --- /dev/null +++ b/shell/platform/ohos/ohos_touch_processor.cpp @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/ohos_touch_processor.h" +#include "flutter/fml/trace_event.h" +#include "flutter/lib/ui/window/pointer_data_packet.h" +#include "flutter/shell/platform/ohos/ohos_shell_holder.h" + +namespace flutter { + +constexpr int MSEC_PER_SECOND = 1000; +constexpr int PER_POINTER_MEMBER = 10; +constexpr int CHANGES_POINTER_MEMBER = 10; +constexpr int TOUCH_EVENT_ADDITIONAL_ATTRIBUTES = 4; + +PointerData::Change OhosTouchProcessor::getPointerChangeForAction( + int maskedAction) { + switch (maskedAction) { + case OH_NATIVEXCOMPONENT_DOWN: + return PointerData::Change::kDown; + case OH_NATIVEXCOMPONENT_UP: + return PointerData::Change::kUp; + case OH_NATIVEXCOMPONENT_CANCEL: + return PointerData::Change::kCancel; + case OH_NATIVEXCOMPONENT_MOVE: + return PointerData::Change::kMove; + } + return PointerData::Change::kCancel; +} + +PointerData::Change OhosTouchProcessor::getPointerChangeForMouseAction( + OH_NativeXComponent_MouseEventAction mouseAction) { + switch (mouseAction) { + case OH_NATIVEXCOMPONENT_MOUSE_PRESS: + return PointerData::Change::kDown; + case OH_NATIVEXCOMPONENT_MOUSE_RELEASE: + return PointerData::Change::kUp; + case OH_NATIVEXCOMPONENT_MOUSE_MOVE: + return PointerData::Change::kMove; + default: + return PointerData::Change::kCancel; + } +} + +PointerButtonMouse OhosTouchProcessor::getPointerButtonFromMouse( + OH_NativeXComponent_MouseEventButton mouseButton) { + switch (mouseButton) { + case OH_NATIVEXCOMPONENT_LEFT_BUTTON: + return kPointerButtonMousePrimary; + case OH_NATIVEXCOMPONENT_RIGHT_BUTTON: + return kPointerButtonMouseSecondary; + case OH_NATIVEXCOMPONENT_MIDDLE_BUTTON: + return kPointerButtonMouseMiddle; + case OH_NATIVEXCOMPONENT_BACK_BUTTON: + return kPointerButtonMouseBack; + case OH_NATIVEXCOMPONENT_FORWARD_BUTTON: + return kPointerButtonMouseForward; + default: + return kPointerButtonMousePrimary; + } +} + +PointerData::DeviceKind OhosTouchProcessor::getPointerDeviceTypeForToolType( + int toolType) { + switch (toolType) { + case OH_NATIVEXCOMPONENT_TOOL_TYPE_FINGER: + return PointerData::DeviceKind::kTouch; + case OH_NATIVEXCOMPONENT_TOOL_TYPE_PEN: + return PointerData::DeviceKind::kStylus; + case OH_NATIVEXCOMPONENT_TOOL_TYPE_RUBBER: + return PointerData::DeviceKind::kInvertedStylus; + case OH_NATIVEXCOMPONENT_TOOL_TYPE_BRUSH: + return PointerData::DeviceKind::kStylus; + case OH_NATIVEXCOMPONENT_TOOL_TYPE_PENCIL: + return PointerData::DeviceKind::kStylus; + case OH_NATIVEXCOMPONENT_TOOL_TYPE_AIRBRUSH: + return PointerData::DeviceKind::kStylus; + case OH_NATIVEXCOMPONENT_TOOL_TYPE_MOUSE: + return PointerData::DeviceKind::kMouse; + case OH_NATIVEXCOMPONENT_TOOL_TYPE_LENS: + return PointerData::DeviceKind::kTouch; + case OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN: + return PointerData::DeviceKind::kTouch; + } + return PointerData::DeviceKind::kTouch; +} + +std::shared_ptr OhosTouchProcessor::packagePacketData( + std::unique_ptr touchPacket) { + if (touchPacket == nullptr) { + return nullptr; + } + int numPoints = touchPacket->touchEventInput->numPoints; + int offset = 0; + int size = CHANGES_POINTER_MEMBER + PER_POINTER_MEMBER * numPoints + + TOUCH_EVENT_ADDITIONAL_ATTRIBUTES; + std::shared_ptr package(new std::string[size]); + + package[offset++] = std::to_string(touchPacket->touchEventInput->numPoints); + + package[offset++] = std::to_string(touchPacket->touchEventInput->id); + package[offset++] = std::to_string(touchPacket->touchEventInput->screenX); + package[offset++] = std::to_string(touchPacket->touchEventInput->screenY); + package[offset++] = std::to_string(touchPacket->touchEventInput->x); + package[offset++] = std::to_string(touchPacket->touchEventInput->y); + package[offset++] = std::to_string(touchPacket->touchEventInput->type); + package[offset++] = std::to_string(touchPacket->touchEventInput->size); + package[offset++] = std::to_string(touchPacket->touchEventInput->force); + package[offset++] = std::to_string(touchPacket->touchEventInput->deviceId); + package[offset++] = std::to_string(touchPacket->touchEventInput->timeStamp); + for (int i = 0; i < numPoints; i++) { + package[offset++] = + std::to_string(touchPacket->touchEventInput->touchPoints[i].id); + package[offset++] = + std::to_string(touchPacket->touchEventInput->touchPoints[i].screenX); + package[offset++] = + std::to_string(touchPacket->touchEventInput->touchPoints[i].screenY); + package[offset++] = + std::to_string(touchPacket->touchEventInput->touchPoints[i].x); + package[offset++] = + std::to_string(touchPacket->touchEventInput->touchPoints[i].y); + package[offset++] = + std::to_string(touchPacket->touchEventInput->touchPoints[i].type); + package[offset++] = + std::to_string(touchPacket->touchEventInput->touchPoints[i].size); + package[offset++] = + std::to_string(touchPacket->touchEventInput->touchPoints[i].force); + package[offset++] = + std::to_string(touchPacket->touchEventInput->touchPoints[i].timeStamp); + package[offset++] = + std::to_string(touchPacket->touchEventInput->touchPoints[i].isPressed); + } + package[offset++] = std::to_string(touchPacket->toolTypeInput); + package[offset++] = std::to_string(touchPacket->tiltX); + package[offset++] = std::to_string(touchPacket->tiltY); + return package; +} + +void OhosTouchProcessor::HandleTouchEvent( + int64_t shell_holderID, + OH_NativeXComponent* component, + OH_NativeXComponent_TouchEvent* touchEvent) { + if (touchEvent == nullptr) { + return; + } + FML_TRACE_EVENT("flutter", "HandleTouchEvent", "timeStamp", + touchEvent->timeStamp); + const int numTouchPoints = 1; + std::unique_ptr packet = + std::make_unique(numTouchPoints); + PointerData pointerData; + pointerData.Clear(); + pointerData.embedder_id = touchEvent->id; + pointerData.time_stamp = touchEvent->timeStamp / MSEC_PER_SECOND; + pointerData.change = getPointerChangeForAction(touchEvent->type); + pointerData.physical_y = touchEvent->y; + pointerData.physical_x = touchEvent->x; + // Delta will be generated in pointer_data_packet_converter.cc. + pointerData.physical_delta_x = 0.0; + pointerData.physical_delta_y = 0.0; + pointerData.device = touchEvent->id; + // Pointer identifier will be generated in pointer_data_packet_converter.cc. + pointerData.pointer_identifier = 0; + // XComponent not support Scroll + pointerData.signal_kind = PointerData::SignalKind::kNone; + pointerData.scroll_delta_x = 0.0; + pointerData.scroll_delta_y = 0.0; + pointerData.pressure = touchEvent->force; + pointerData.pressure_max = 1.0; + pointerData.pressure_min = 0.0; + OH_NativeXComponent_TouchPointToolType toolType; + OH_NativeXComponent_GetTouchPointToolType(component, 0, &toolType); + pointerData.kind = getPointerDeviceTypeForToolType(toolType); + if (pointerData.kind == PointerData::DeviceKind::kTouch) { + if (pointerData.change == PointerData::Change::kDown || + pointerData.change == PointerData::Change::kMove) { + pointerData.buttons = kPointerButtonTouchContact; + } + } + pointerData.pan_x = 0.0; + pointerData.pan_y = 0.0; + // Delta will be generated in pointer_data_packet_converter.cc. + pointerData.pan_delta_x = 0.0; + pointerData.pan_delta_y = 0.0; + // The contact area between the fingerpad and the screen + pointerData.size = touchEvent->size; + pointerData.scale = 1.0; + pointerData.rotation = 0.0; + packet->SetPointerData(0, pointerData); + auto ohos_shell_holder = reinterpret_cast(shell_holderID); + ohos_shell_holder->GetPlatformView()->DispatchPointerDataPacket( + std::move(packet)); + + // For DFX + fml::closure task = [timeStampDFX = touchEvent->timeStamp](void) { + FML_TRACE_EVENT("flutter", "HandleTouchEventUI", "timeStamp", timeStampDFX); + }; + ohos_shell_holder->GetPlatformView()->RunTask(OhosThreadType::kUI, task); + PlatformViewOnTouchEvent(shell_holderID, toolType, component, touchEvent); +} + +void OhosTouchProcessor::PlatformViewOnTouchEvent( + int64_t shellHolderID, + OH_NativeXComponent_TouchPointToolType toolType, + OH_NativeXComponent* component, + OH_NativeXComponent_TouchEvent* touchEvent) { + int numPoints = touchEvent->numPoints; + float tiltX = 0.0; + float tiltY = 0.0; + OH_NativeXComponent_GetTouchPointTiltX(component, 0, &tiltX); + OH_NativeXComponent_GetTouchPointTiltY(component, 0, &tiltY); + std::unique_ptr touchPacket = + std::make_unique(); + touchPacket->touchEventInput = touchEvent; + touchPacket->toolTypeInput = toolType; + touchPacket->tiltX = tiltX; + touchPacket->tiltX = tiltY; + + std::shared_ptr touchPacketString = + packagePacketData(std::move(touchPacket)); + int size = CHANGES_POINTER_MEMBER + PER_POINTER_MEMBER * numPoints + + TOUCH_EVENT_ADDITIONAL_ATTRIBUTES; + auto ohos_shell_holder = reinterpret_cast(shellHolderID); + ohos_shell_holder->GetPlatformView()->OnTouchEvent(touchPacketString, size); +} + +void OhosTouchProcessor::HandleMouseEvent( + int64_t shell_holderID, + OH_NativeXComponent* component, + OH_NativeXComponent_MouseEvent mouseEvent, + double offsetY) { + const int numTouchPoints = 1; + std::unique_ptr packet = + std::make_unique(numTouchPoints); + PointerData pointerData; + pointerData.Clear(); + pointerData.embedder_id = mouseEvent.button; + pointerData.time_stamp = mouseEvent.timestamp / MSEC_PER_SECOND; + pointerData.change = getPointerChangeForMouseAction(mouseEvent.action); + pointerData.physical_y = mouseEvent.y; + pointerData.physical_x = mouseEvent.x; + // Delta will be generated in pointer_data_packet_converter.cc. + pointerData.physical_delta_x = 0.0; + pointerData.physical_delta_y = 0.0; + pointerData.device = mouseEvent.button; + // Pointer identifier will be generated in pointer_data_packet_converter.cc. + pointerData.pointer_identifier = 0; + // XComponent not support Scroll + // now it's support + pointerData.signal_kind = offsetY != 0 ? PointerData::SignalKind::kScroll + : PointerData::SignalKind::kNone; + pointerData.scroll_delta_x = 0.0; + pointerData.scroll_delta_y = offsetY; + pointerData.pressure = 0.0; + pointerData.pressure_max = 1.0; + pointerData.pressure_min = 0.0; + pointerData.kind = PointerData::DeviceKind::kTouch; + pointerData.buttons = getPointerButtonFromMouse(mouseEvent.button); + // hover support + if (mouseEvent.button == OH_NATIVEXCOMPONENT_NONE_BUTTON && + pointerData.change == PointerData::Change::kMove) { + pointerData.change = PointerData::Change::kHover; + pointerData.kind = PointerData::DeviceKind::kMouse; + pointerData.buttons = 0; + } + pointerData.pan_x = 0.0; + pointerData.pan_y = 0.0; + // Delta will be generated in pointer_data_packet_converter.cc. + pointerData.pan_delta_x = 0.0; + pointerData.pan_delta_y = 0.0; + // The contact area between the fingerpad and the screen + pointerData.size = 0.0; + pointerData.scale = 1.0; + pointerData.rotation = 0.0; + packet->SetPointerData(0, pointerData); + auto ohos_shell_holder = reinterpret_cast(shell_holderID); + ohos_shell_holder->GetPlatformView()->DispatchPointerDataPacket( + std::move(packet)); + return; +} + +void OhosTouchProcessor::HandleVirtualTouchEvent( + int64_t shell_holderID, + OH_NativeXComponent* component, + OH_NativeXComponent_TouchEvent* touchEvent) { + int numPoints = touchEvent->numPoints; + float tiltX = 0.0; + float tiltY = 0.0; + auto ohos_shell_holder = reinterpret_cast(shell_holderID); + OH_NativeXComponent_TouchPointToolType toolType; + OH_NativeXComponent_GetTouchPointToolType(component, 0, &toolType); + OH_NativeXComponent_GetTouchPointTiltX(component, 0, &tiltX); + OH_NativeXComponent_GetTouchPointTiltY(component, 0, &tiltY); + std::unique_ptr touchPacket = + std::make_unique(); + touchPacket->touchEventInput = touchEvent; + touchPacket->toolTypeInput = toolType; + touchPacket->tiltX = tiltX; + touchPacket->tiltX = tiltY; + + std::shared_ptr touchPacketString = + packagePacketData(std::move(touchPacket)); + int size = CHANGES_POINTER_MEMBER + PER_POINTER_MEMBER * numPoints + + TOUCH_EVENT_ADDITIONAL_ATTRIBUTES; + ohos_shell_holder->GetPlatformView()->OnTouchEvent(touchPacketString, size); + return; +} +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/ohos_touch_processor.h b/shell/platform/ohos/ohos_touch_processor.h new file mode 100644 index 0000000000000000000000000000000000000000..7cc7fc42f7273d45c3d996b67f6bdb0a1be625e4 --- /dev/null +++ b/shell/platform/ohos/ohos_touch_processor.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_TOUCH_PROCESSOR_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_TOUCH_PROCESSOR_H_ +#include +#include +#include +#include "flutter/lib/ui/window/pointer_data.h" +#include "napi_common.h" + +namespace flutter { + +class OhosTouchProcessor { + public: + typedef struct { + OH_NativeXComponent_TouchEvent* touchEventInput; + OH_NativeXComponent_TouchPointToolType toolTypeInput; + float tiltX; + float tiltY; + } TouchPacket; + + public: + void HandleTouchEvent(int64_t shell_holderID, + OH_NativeXComponent* component, + OH_NativeXComponent_TouchEvent* touchEvent); + void HandleMouseEvent(int64_t shell_holderID, + OH_NativeXComponent* component, + OH_NativeXComponent_MouseEvent mouseEvent, + double offsetY); + void HandleVirtualTouchEvent(int64_t shell_holderID, + OH_NativeXComponent* component, + OH_NativeXComponent_TouchEvent* touchEvent); + flutter::PointerData::Change getPointerChangeForAction(int maskedAction); + flutter::PointerData::DeviceKind getPointerDeviceTypeForToolType( + int toolType); + flutter::PointerData::Change getPointerChangeForMouseAction( + OH_NativeXComponent_MouseEventAction mouseAction); + PointerButtonMouse getPointerButtonFromMouse( + OH_NativeXComponent_MouseEventButton mouseButton); + + public: + OH_NativeXComponent_TouchPointToolType touchType_; + + private: + std::shared_ptr packagePacketData( + std::unique_ptr touchPacket); + + void PlatformViewOnTouchEvent(int64_t shellHolderID, + OH_NativeXComponent_TouchPointToolType toolType, + OH_NativeXComponent* component, + OH_NativeXComponent_TouchEvent* touchEvent); +}; +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_TOUCH_PROCESSOR_H_ \ No newline at end of file diff --git a/shell/platform/ohos/ohos_unified_surface.cpp b/shell/platform/ohos/ohos_unified_surface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73c3a0c954d6b7f9ee95af2336d8e7b4f37284bb --- /dev/null +++ b/shell/platform/ohos/ohos_unified_surface.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/ohos_unified_surface.h" +namespace flutter { + +OHOSUnifiedSurface::OHOSUnifiedSurface() {} + + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/ohos_unified_surface.h b/shell/platform/ohos/ohos_unified_surface.h new file mode 100644 index 0000000000000000000000000000000000000000..d3de35ac455cb419f74ab9ca0af4c77dc4493671 --- /dev/null +++ b/shell/platform/ohos/ohos_unified_surface.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_UNIFIED_SURFACE_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_UNIFIED_SURFACE_H_ + +#include +#include "flutter/flow/surface.h" +#include "flutter/shell/platform/ohos/context/ohos_context.h" +#include "flutter/shell/platform/ohos/ohos_surface_gl_skia.h" +#include "flutter/shell/platform/ohos/surface/ohos_native_window.h" +#include "flutter/shell/platform/ohos/surface/ohos_surface.h" +#include "third_party/skia/include/core/SkSize.h" + +namespace flutter { + +class OHOSUnifiedSurface : public GPUSurfaceGLDelegate, public OHOSSurface { + public: + ~OHOSUnifiedSurface() {} + bool IsValid() {} + void TeardownOnScreenContext() {} + + bool OnScreenSurfaceResize(const SkISize& size) {} + + bool ResourceContextMakeCurrent() {} + + bool ResourceContextClearCurrent() {} + + bool SetNativeWindow(fml::RefPtr window) {} + + std::unique_ptr CreateSnapshotSurface() + + std::unique_ptr CreateGPUSurface( + GrDirectContext* gr_context = nullptr) {} + + std::shared_ptr GetImpellerContext() {} + + std::unique_ptr GLContextMakeCurrent() {} + + bool GLContextClearCurrent() {} + + void GLContextSetDamageRegion(const std::optional& region) {} + + bool GLContextPresent(const GLPresentInfo& present_info) {} + + GLFBOInfo GLContextFBO(GLFrameInfo frame_info) {} + + bool GLContextFBOResetAfterPresent() {} + + SurfaceFrame::FramebufferInfo GLContextFramebufferInfo() {} + + SkMatrix GLContextSurfaceTransformation() {} + + sk_sp GetGLInterface() {} + + static sk_sp GetDefaultPlatformGLInterface() {} + + using GLProcResolver = std::function; + + GLProcResolver GetGLProcResolver() {} + + bool AllowsDrawingWhenGpuDisabled() {} +}; +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_UNIFIED_SURFACE_H_ \ No newline at end of file diff --git a/shell/platform/ohos/ohos_xcomponent_adapter.cpp b/shell/platform/ohos/ohos_xcomponent_adapter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..71e4ffd7e05c9d0d7c3b9d8f0127e816022134af --- /dev/null +++ b/shell/platform/ohos/ohos_xcomponent_adapter.cpp @@ -0,0 +1,770 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "ohos_xcomponent_adapter.h" +#include +#include +#include +#include +#include +#include +#include +#include "accessibility/ohos_semantics_bridge.h" +#include "fml/trace_event.h" +#include "napi/platform_view_ohos_napi.h" +#include "ohos_logging.h" +#include "ohos_shell_holder.h" +#include "shell/common/shell.h" +#include "types.h" +namespace flutter { + +bool g_isMouseLeftActive = false; +double g_scrollDistance = 0.0; +double g_resizeRate = 0.8; + +XComponentAdapter XComponentAdapter::mXComponentAdapter; + +XComponentAdapter::XComponentAdapter(/* args */) {} + +XComponentAdapter::~XComponentAdapter() {} + +XComponentAdapter* XComponentAdapter::GetInstance() { + return &XComponentAdapter::mXComponentAdapter; +} + +bool XComponentAdapter::Export(napi_env env, napi_value exports) { + napi_status status; + napi_value exportInstance = nullptr; + OH_NativeXComponent* nativeXComponent = nullptr; + int32_t ret; + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + + status = napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, + &exportInstance); + LOGD("napi_get_named_property,status = %{public}d", status); + if (status != napi_ok) { + return false; + } + + status = napi_unwrap(env, exportInstance, + reinterpret_cast(&nativeXComponent)); + LOGD("napi_unwrap,status = %{public}d", status); + if (status != napi_ok) { + return false; + } + + ret = OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize); + LOGD("NativeXComponent id:%{public}s", idStr); + if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + return false; + } + std::string id(idStr); + auto context = XComponentAdapter::GetInstance(); + if (context) { + context->SetNativeXComponent(id, nativeXComponent); + return true; + } + + return false; +} + +void XComponentAdapter::SetNativeXComponent( + std::string& id, + OH_NativeXComponent* nativeXComponent) { + std::lock_guard lock(xcomponentMap_mutex_); + auto iter = xcomponetMap_.find(id); + if (iter == xcomponetMap_.end()) { + XComponentBase* xcomponet = new XComponentBase(id); + xcomponetMap_[id] = xcomponet; + } + + iter = xcomponetMap_.find(id); + if (iter != xcomponetMap_.end()) { + iter->second->SetNativeXComponent(nativeXComponent); + } +} + +void XComponentAdapter::AttachFlutterEngine(std::string& id, + std::string& shellholderId) { + TRACE_EVENT1("flutter", "AttachFlutterEngine", "ShellID", + shellholderId.c_str()); + std::lock_guard lock(xcomponentMap_mutex_); + auto iter = xcomponetMap_.find(id); + if (iter == xcomponetMap_.end()) { + XComponentBase* xcomponet = new XComponentBase(id); + xcomponetMap_[id] = xcomponet; + } + + auto findIter = xcomponetMap_.find(id); + if (findIter != xcomponetMap_.end()) { + findIter->second->AttachFlutterEngine(shellholderId); + } + SetCurrentXcomponentId(id); +} + +void XComponentAdapter::PreDraw(std::string& id, + std::string& shellholderId, + int width, + int height) { + std::lock_guard lock(xcomponentMap_mutex_); + auto iter = xcomponetMap_.find(id); + if (iter == xcomponetMap_.end()) { + XComponentBase* xcomponet = new XComponentBase(id); + xcomponetMap_[id] = xcomponet; + } + + auto findIter = xcomponetMap_.find(id); + if (findIter != xcomponetMap_.end()) { + findIter->second->PreDraw(shellholderId, width, height); + } +} + +void XComponentAdapter::DetachFlutterEngine(std::string& id) { + std::lock_guard lock(xcomponentMap_mutex_); + auto iter = xcomponetMap_.find(id); + if (iter != xcomponetMap_.end()) { + iter->second->DetachFlutterEngine(); + } + if (current_xcomponent_id_ == id) { + SetCurrentXcomponentId(""); + } +} + +void XComponentAdapter::OnMouseWheel(std::string& id, mouseWheelEvent event) { + std::lock_guard lock(xcomponentMap_mutex_); + auto iter = xcomponetMap_.find(id); + if (iter != xcomponetMap_.end()) { + iter->second->OnDispatchMouseWheelEvent(event); + } +} + +// It must be invoked within the xcomponentMap_mutex_ lock. +XComponentBase* XComponentAdapter::GetCurrentXcomponent() { + auto iter = xcomponetMap_.find(current_xcomponent_id_); + if (iter != xcomponetMap_.end()) { + return xcomponetMap_[current_xcomponent_id_]; + } + return nullptr; +} + +void XComponentAdapter::SetCurrentXcomponentId(std::string id) { + current_xcomponent_id_ = std::move(id); +} + +static int32_t SetNativeWindowOpt(OHNativeWindow* nativeWindow, + int32_t width, + int32_t height) { + // Set the read and write scenarios of the native window buffer. + int code = SET_USAGE; + int32_t ret = OH_NativeWindow_NativeWindowHandleOpt( + nativeWindow, code, + NATIVEBUFFER_USAGE_HW_TEXTURE | NATIVEBUFFER_USAGE_HW_RENDER | + NATIVEBUFFER_USAGE_MEM_DMA); + if (ret) { + LOGE( + "Set NativeWindow Usage Failed :window:%{public}p ,w:%{public}d x " + "%{public}d:%{public}d", + nativeWindow, width, height, ret); + } + // Set the width and height of the native window buffer. + code = SET_BUFFER_GEOMETRY; + ret = + OH_NativeWindow_NativeWindowHandleOpt(nativeWindow, code, width, height); + if (ret) { + LOGE( + "Set NativeWindow GEOMETRY Failed :window:%{public}p ,w:%{public}d x " + "%{public}d:%{public}d", + nativeWindow, width, height, ret); + } + // Set the format of the native window buffer. + code = SET_FORMAT; + int32_t format = kPixelFmtRgba8888; + + ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow, code, format); + if (ret) { + LOGE( + "Set NativeWindow kPixelFmtRgba8888 Failed :window:%{public}p " + ",w:%{public}d x %{public}d:%{public}d", + nativeWindow, width, height, ret); + } + return ret; +} + +void OnSurfaceCreatedCB(OH_NativeXComponent* component, void* window) { + std::lock_guard lock( + XComponentAdapter::GetInstance()->xcomponentMap_mutex_); + for (auto it : XComponentAdapter::GetInstance()->xcomponetMap_) { + if (it.second->nativeXComponent_ == component) { + LOGD("OnSurfaceCreatedCB is called"); + it.second->OnSurfaceCreated(component, window); + } + } +} + +void OnSurfaceChangedCB(OH_NativeXComponent* component, void* window) { + std::lock_guard lock( + XComponentAdapter::GetInstance()->xcomponentMap_mutex_); + for (auto it : XComponentAdapter::GetInstance()->xcomponetMap_) { + if (it.second->nativeXComponent_ == component) { + it.second->OnSurfaceChanged(component, window); + } + } +} + +void OnSurfaceDestroyedCB(OH_NativeXComponent* component, void* window) { + std::lock_guard lock( + XComponentAdapter::GetInstance()->xcomponentMap_mutex_); + for (auto it = XComponentAdapter::GetInstance()->xcomponetMap_.begin(); + it != XComponentAdapter::GetInstance()->xcomponetMap_.end();) { + if (it->second->nativeXComponent_ == component) { + auto component_id = it->first; + if (it->second == + XComponentAdapter::GetInstance()->GetCurrentXcomponent()) { + XComponentAdapter::GetInstance()->SetCurrentXcomponentId(""); + } + it->second->OnSurfaceDestroyed(component, window); + delete it->second; + it = XComponentAdapter::GetInstance()->xcomponetMap_.erase(it); + } else { + ++it; + } + } +} +void DispatchTouchEventCB(OH_NativeXComponent* component, void* window) { + std::lock_guard lock( + XComponentAdapter::GetInstance()->xcomponentMap_mutex_); + for (auto it : XComponentAdapter::GetInstance()->xcomponetMap_) { + if (it.second->nativeXComponent_ == component) { + it.second->OnDispatchTouchEvent(component, window); + } + } +} + +void DispatchMouseEventCB(OH_NativeXComponent* component, void* window) { + std::lock_guard lock( + XComponentAdapter::GetInstance()->xcomponentMap_mutex_); + for (auto it : XComponentAdapter::GetInstance()->xcomponetMap_) { + if (it.second->nativeXComponent_ == component) { + it.second->OnDispatchMouseEvent(component, window); + } + } +} + +void DispatchHoverEventCB(OH_NativeXComponent* component, bool isHover) { + LOGD("XComponentManger::DispatchHoverEventCB"); +} + +void XComponentBase::BindXComponentCallback() { + callback_.OnSurfaceCreated = OnSurfaceCreatedCB; + callback_.OnSurfaceChanged = OnSurfaceChangedCB; + callback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB; + callback_.DispatchTouchEvent = DispatchTouchEventCB; + mouseCallback_.DispatchMouseEvent = DispatchMouseEventCB; + mouseCallback_.DispatchHoverEvent = DispatchHoverEventCB; +} + +/** Called when need to get element infos based on a specified node. */ +static int32_t FindAccessibilityNodeInfosByIdCallback( + int64_t elementId, + ArkUI_AccessibilitySearchMode mode, + int32_t requestId, + ArkUI_AccessibilityElementInfoList* elementList) { + LOGD( + "accessibilityProviderCallback_.FindAccessibilityNodeInfosById mode " + "%{public}d id %{public}ld", + mode, elementId); + std::lock_guard lock( + XComponentAdapter::GetInstance()->xcomponentMap_mutex_); + auto xcomp = XComponentAdapter::GetInstance()->GetCurrentXcomponent(); + if (xcomp != nullptr) { + return xcomp->FindAccessibilityNodeInfosById(elementId, mode, requestId, + elementList); + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +/** Called when need to get element infos based on a specified node and text + * content. */ +int32_t FindAccessibilityNodeInfosByTextCallback( + int64_t elementId, + const char* text, + int32_t requestId, + ArkUI_AccessibilityElementInfoList* elementList) { + LOGD("accessibilityProviderCallback_.FindAccessibilityNodeInfosByText"); + std::lock_guard lock( + XComponentAdapter::GetInstance()->xcomponentMap_mutex_); + auto xcomp = XComponentAdapter::GetInstance()->GetCurrentXcomponent(); + if (xcomp != nullptr) { + return xcomp->FindAccessibilityNodeInfosByText(elementId, text, requestId, + elementList); + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +/** Called when need to get the focused element info based on a specified node. + */ +int32_t FindFocusedAccessibilityNodeCallback( + int64_t elementId, + ArkUI_AccessibilityFocusType focusType, + int32_t requestId, + ArkUI_AccessibilityElementInfo* elementinfo) { + LOGD("accessibilityProviderCallback_.FindFocusedAccessibilityNode"); + std::lock_guard lock( + XComponentAdapter::GetInstance()->xcomponentMap_mutex_); + auto xcomp = XComponentAdapter::GetInstance()->GetCurrentXcomponent(); + if (xcomp != nullptr) { + return xcomp->FindFocusedAccessibilityNode(elementId, focusType, requestId, + elementinfo); + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +/** Query the node that can be focused based on the reference node. Query the + * next node that can be focused based on the mode and direction. */ +int32_t FindNextFocusAccessibilityNodeCallback( + int64_t elementId, + ArkUI_AccessibilityFocusMoveDirection direction, + int32_t requestId, + ArkUI_AccessibilityElementInfo* elementList) { + LOGD("accessibilityProviderCallback_.FindNextFocusAccessibilityNode"); + std::lock_guard lock( + XComponentAdapter::GetInstance()->xcomponentMap_mutex_); + auto xcomp = XComponentAdapter::GetInstance()->GetCurrentXcomponent(); + if (xcomp != nullptr) { + return xcomp->FindNextFocusAccessibilityNode(elementId, direction, + requestId, elementList); + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +/** Performing the Action operation on a specified node. */ +int32_t ExecuteAccessibilityActionCallback( + int64_t elementId, + ArkUI_Accessibility_ActionType action, + ArkUI_AccessibilityActionArguments* actionArguments, + int32_t requestId) { + LOGD("accessibilityProviderCallback_.ExecuteAccessibilityAction"); + std::lock_guard lock( + XComponentAdapter::GetInstance()->xcomponentMap_mutex_); + auto xcomp = XComponentAdapter::GetInstance()->GetCurrentXcomponent(); + if (xcomp != nullptr) { + return xcomp->ExecuteAccessibilityAction(elementId, action, actionArguments, + requestId); + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +/** Clears the focus status of the currently focused node */ +int32_t ClearFocusedFocusAccessibilityNodeCallback() { + std::lock_guard lock( + XComponentAdapter::GetInstance()->xcomponentMap_mutex_); + auto xcomp = XComponentAdapter::GetInstance()->GetCurrentXcomponent(); + LOGD("accessibilityProviderCallback_.ClearFocusedFocusAccessibilityNode"); + if (xcomp != nullptr) { + return xcomp->ClearFocusedFocusAccessibilityNode(0); + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +/** Queries the current cursor position of a specified node. */ +int32_t GetAccessibilityNodeCursorPositionCallback(int64_t elementId, + int32_t requestId, + int32_t* index) { + LOGD("accessibilityProviderCallback_.GetAccessibilityNodeCursorPosition"); + std::lock_guard lock( + XComponentAdapter::GetInstance()->xcomponentMap_mutex_); + auto xcomp = XComponentAdapter::GetInstance()->GetCurrentXcomponent(); + if (xcomp != nullptr) { + return xcomp->GetAccessibilityNodeCursorPosition(elementId, requestId, + index); + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +void XComponentBase::BindAccessibilityProviderCallback() { + accessibilityProviderCallback_.findAccessibilityNodeInfosById = + FindAccessibilityNodeInfosByIdCallback; + accessibilityProviderCallback_.findAccessibilityNodeInfosByText = + FindAccessibilityNodeInfosByTextCallback; + accessibilityProviderCallback_.findFocusedAccessibilityNode = + FindFocusedAccessibilityNodeCallback; + accessibilityProviderCallback_.findNextFocusAccessibilityNode = + FindNextFocusAccessibilityNodeCallback; + accessibilityProviderCallback_.executeAccessibilityAction = + ExecuteAccessibilityActionCallback; + accessibilityProviderCallback_.clearFocusedFocusAccessibilityNode = + ClearFocusedFocusAccessibilityNodeCallback; + accessibilityProviderCallback_.getAccessibilityNodeCursorPosition = + GetAccessibilityNodeCursorPositionCallback; +} + +XComponentBase::XComponentBase(std::string id) { + id_ = id; + is_engine_attached_ = false; +} + +XComponentBase::~XComponentBase() {} + +void XComponentBase::AttachFlutterEngine(std::string shellholderId) { + LOGD( + "XComponentManger::AttachFlutterEngine xcomponentId:%{public}s, " + "shellholderId:%{public}s", + id_.c_str(), shellholderId.c_str()); + shellholderId_ = shellholderId; + shellholder_ptr_ = + reinterpret_cast(std::stoll(shellholderId_)); + is_engine_attached_ = true; + if (window_ != nullptr) { + if (provider_ != nullptr && shellholder_ptr_) { + shellholder_ptr_->SetAccessibilityProvider(provider_); + } + PlatformViewOHOSNapi::SurfaceCreated(std::stoll(shellholderId_), window_, + width_, height_); + is_surface_present_ = true; + } +} + +void XComponentBase::PreDraw(std::string shellholderId, int width, int height) { + LOGD( + "AttachFlutterEngine XComponentBase is not attached---use preload " + "%{public}d %{public}d", + width, height); + shellholderId_ = std::move(shellholderId); + shellholder_ptr_ = + reinterpret_cast(std::stoll(shellholderId_)); + is_engine_attached_ = true; + if (is_surface_preloaded_) { + return; + } + PlatformViewOHOSNapi::SurfacePreload(std::stoll(shellholderId_), width, + height); + is_surface_preloaded_ = true; +} + +void XComponentBase::DetachFlutterEngine() { + LOGD( + "XComponentManger::DetachFlutterEngine xcomponentId:%{public}s, " + "shellholderId:%{public}s", + id_.c_str(), shellholderId_.c_str()); + if (window_ != nullptr) { + PlatformViewOHOSNapi::SurfaceDestroyed(std::stoll(shellholderId_)); + } else { + LOGE("DetachFlutterEngine XComponentBase is not attached"); + } + + if (provider_ != nullptr && shellholder_ptr_) { + shellholder_ptr_->SetAccessibilityProvider(nullptr); + } + + shellholderId_ = ""; + shellholder_ptr_ = nullptr; + is_engine_attached_ = false; + is_surface_present_ = false; + is_surface_preloaded_ = false; +} + +void XComponentBase::SetNativeXComponent( + OH_NativeXComponent* nativeXComponent) { + nativeXComponent_ = nativeXComponent; + if (nativeXComponent_ != nullptr) { + BindXComponentCallback(); + OH_NativeXComponent_RegisterCallback(nativeXComponent_, &callback_); + OH_NativeXComponent_RegisterMouseEventCallback(nativeXComponent_, + &mouseCallback_); + } +} + +ArkUI_AccessibilityProvider* +XComponentBase::GetArkUIAccessibilityServiceProvider( + OH_NativeXComponent* nativeXComponent) { + BindAccessibilityProviderCallback(); + ArkUI_AccessibilityProvider* provider = nullptr; + int32_t ret = OH_NativeXComponent_GetNativeAccessibilityProvider( + nativeXComponent, &provider); + if (ret != 0) { + LOGE("OH_NativeXComponent_GetNativeAccessibilityProvider is failed"); + return nullptr; + } + ret = OH_ArkUI_AccessibilityProviderRegisterCallback( + provider, &accessibilityProviderCallback_); + if (ret != 0) { + LOGE("OH_ArkUI_AccessibilityProviderRegisterCallback is failed"); + return nullptr; + } + LOGI("XComponentBase::GetArkUIAccessibilityServiceProvider -> finished"); + return provider; +} + +void XComponentBase::OnSurfaceCreated(OH_NativeXComponent* component, + void* window) { + LOGD( + "XComponentManger::OnSurfaceCreated window = %{public}p component = " + "%{public}p", + window, component); + TRACE_EVENT1("flutter", "OnSurfaceCreated", "ShellID", + shellholderId_.c_str()); + window_ = window; + int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window, + &width_, &height_); + if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + LOGD("XComponent Current width:%{public}d,height:%{public}d", + static_cast(width_), static_cast(height_)); + } else { + LOGE("GetXComponentSize result:%{public}d", ret); + } + + // This setting ensures that the soft keyboard does not automatically dismiss + // when the Xcomponent regains focus. + ret = OH_NativeXComponent_SetNeedSoftKeyboard(component, true); + if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + LOGE("OH_NativeXComponent_SetNeedSoftKeyboard failed result:%{public}d", + ret); + } + + LOGD("OnSurfaceCreated,window.size:%{public}d,%{public}d", (int)width_, + (int)height_); + ret = SetNativeWindowOpt((OHNativeWindow*)window, width_, height_); + if (ret) { + LOGE("SetNativeWindowOpt failed:%{public}d", ret); + } + + provider_ = GetArkUIAccessibilityServiceProvider(nativeXComponent_); + if (is_engine_attached_) { + ret = OH_NativeWindow_NativeObjectReference(window); + if (ret) { + LOGE("NativeObjectReference failed:%{public}d", ret); + } + if (provider_ != nullptr && shellholder_ptr_) { + shellholder_ptr_->SetAccessibilityProvider(provider_); + } else { + LOGE("OnSurfaceCreated AccessibilityProvider is nullptr"); + } + + PlatformViewOHOSNapi::SurfaceCreated(std::stoll(shellholderId_), window, + width_, height_); + is_surface_present_ = true; + } else { + LOGE("OnSurfaceCreated XComponentBase is not attached"); + } +} + +void XComponentBase::OnSurfaceChanged(OH_NativeXComponent* component, + void* window) { + LOGD("XComponentManger::OnSurfaceChanged "); + int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window, + &width_, &height_); + if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + LOGD("XComponent Current width:%{public}d,height:%{public}d", + static_cast(width_), static_cast(height_)); + } + if (is_engine_attached_) { + PlatformViewOHOSNapi::SurfaceChanged(std::stoll(shellholderId_), window, + width_, height_); + } else { + LOGE("OnSurfaceChanged XComponentBase is not attached"); + } +} + +void XComponentBase::OnSurfaceDestroyed(OH_NativeXComponent* component, + void* window) { + window_ = nullptr; + LOGD("XComponentManger::OnSurfaceDestroyed"); + if (is_engine_attached_) { + is_surface_present_ = false; + is_surface_preloaded_ = false; + PlatformViewOHOSNapi::SurfaceDestroyed(std::stoll(shellholderId_)); + int32_t ret = OH_NativeWindow_NativeObjectUnreference(window); + if (ret) { + LOGE("NativeObjectReference failed:%{public}d", ret); + } + + if (provider_ != nullptr && shellholder_ptr_) { + shellholder_ptr_->SetAccessibilityProvider(nullptr); + } + provider_ = nullptr; + + } else { + LOGE("XComponentManger::OnSurfaceDestroyed XComponentBase is not attached"); + } +} + +void XComponentBase::OnDispatchTouchEvent(OH_NativeXComponent* component, + void* window) { + int32_t ret = + OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent_); + if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + if (is_engine_attached_ && is_surface_present_) { + // if this touchEvent triggered by mouse, return + OH_NativeXComponent_EventSourceType sourceType; + int32_t ret2 = OH_NativeXComponent_GetTouchEventSourceType( + component, touchEvent_.id, &sourceType); + if (ret2 == OH_NATIVEXCOMPONENT_RESULT_SUCCESS && + sourceType == OH_NATIVEXCOMPONENT_SOURCE_TYPE_MOUSE) { + ohosTouchProcessor_.HandleVirtualTouchEvent(std::stoll(shellholderId_), + component, &touchEvent_); + return; + } + ohosTouchProcessor_.HandleTouchEvent(std::stoll(shellholderId_), + component, &touchEvent_); + } else { + LOGE( + "XComponentManger::DispatchTouchEvent XComponentBase is not " + "attached"); + } + } +} + +void XComponentBase::OnDispatchMouseEvent(OH_NativeXComponent* component, + void* window) { + OH_NativeXComponent_MouseEvent mouseEvent; + int32_t ret = + OH_NativeXComponent_GetMouseEvent(component, window, &mouseEvent); + if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS && is_engine_attached_) { + if (mouseEvent.button == OH_NATIVEXCOMPONENT_LEFT_BUTTON) { + if (mouseEvent.action == OH_NATIVEXCOMPONENT_MOUSE_PRESS) { + g_isMouseLeftActive = true; + } else if (mouseEvent.action == OH_NATIVEXCOMPONENT_MOUSE_RELEASE) { + g_isMouseLeftActive = false; + } + } + ohosTouchProcessor_.HandleMouseEvent(std::stoll(shellholderId_), component, + mouseEvent, 0.0); + return; + } + LOGE("XComponentManger::DispatchMouseEvent XComponentBase is not attached"); +} + +void XComponentBase::OnDispatchMouseWheelEvent(mouseWheelEvent event) { + std::string shell_holder_str = std::to_string(event.shellHolder); + if (shell_holder_str != shellholderId_) { + return; + } + if (is_engine_attached_) { + if (g_isMouseLeftActive) { + return; + } + if (event.eventType == "actionUpdate") { + OH_NativeXComponent_MouseEvent mouseEvent; + double scrollY = event.offsetY - g_scrollDistance; + g_scrollDistance = event.offsetY; + // fix resize ratio + mouseEvent.x = event.globalX / g_resizeRate; + mouseEvent.y = event.globalY / g_resizeRate; + scrollY = scrollY / g_resizeRate; + mouseEvent.button = OH_NATIVEXCOMPONENT_NONE_BUTTON; + mouseEvent.action = OH_NATIVEXCOMPONENT_MOUSE_NONE; + mouseEvent.timestamp = event.timestamp; + ohosTouchProcessor_.HandleMouseEvent(std::stoll(shellholderId_), nullptr, + mouseEvent, scrollY); + } else { + g_scrollDistance = 0.0; + } + } else { + LOGE( + "XComponentManger::DispatchMouseWheelEvent XComponentBase is not " + "attached"); + } +} + +int32_t XComponentBase::FindAccessibilityNodeInfosById( + int64_t elementId, + ArkUI_AccessibilitySearchMode mode, + int32_t requestId, + ArkUI_AccessibilityElementInfoList* elementList) { + if (shellholder_ptr_) { + return shellholder_ptr_->FillNodesWithSearch(elementId, mode, elementList); + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +int32_t XComponentBase::FindAccessibilityNodeInfosByText( + int64_t elementId, + const char* text, + int32_t requestId, + ArkUI_AccessibilityElementInfoList* elementList) { + if (shellholder_ptr_) { + return shellholder_ptr_->FillNodesWithSearchText(elementId, text, + elementList); + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +int32_t XComponentBase::FindFocusedAccessibilityNode( + int64_t elementId, + ArkUI_AccessibilityFocusType focusType, + int32_t requestId, + ArkUI_AccessibilityElementInfo* elementinfo) { + if (shellholder_ptr_) { + return shellholder_ptr_->FindFocusNode(elementId, focusType, elementinfo); + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +int32_t XComponentBase::FindNextFocusAccessibilityNode( + int64_t elementId, + ArkUI_AccessibilityFocusMoveDirection direction, + int32_t requestId, + ArkUI_AccessibilityElementInfo* elementinfo) { + if (shellholder_ptr_) { + return shellholder_ptr_->FindNextFocusNode(elementId, direction, + elementinfo); + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +int32_t XComponentBase::ExecuteAccessibilityAction( + int64_t elementId, + ArkUI_Accessibility_ActionType action, + ArkUI_AccessibilityActionArguments* actionArguments, + int32_t requestId) { + if (shellholder_ptr_) { + return shellholder_ptr_->ExecuteAction(elementId, action, actionArguments); + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +int32_t XComponentBase::ClearFocusedFocusAccessibilityNode(int64_t id) { + if (shellholder_ptr_) { + return shellholder_ptr_->ClearAccessibilityFocus(id); + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +int32_t XComponentBase::GetAccessibilityNodeCursorPosition(int64_t elementId, + int32_t requestId, + int32_t* index) { + if (shellholder_ptr_) { + return shellholder_ptr_->GetAccessibilityNodeCursorPosition(elementId, + index); + } else { + return ARKUI_ACCESSIBILITY_NATIVE_RESULT_FAILED; + } +} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/ohos_xcomponent_adapter.h b/shell/platform/ohos/ohos_xcomponent_adapter.h new file mode 100644 index 0000000000000000000000000000000000000000..a8e23c246d4fe7266c8e83e53debd4c59cacf045 --- /dev/null +++ b/shell/platform/ohos/ohos_xcomponent_adapter.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_OHOS_XCOMPONENT_ADAPTER_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_OHOS_XCOMPONENT_ADAPTER_H_ +#include +#include +#include +#include +#include "flutter/shell/platform/ohos/napi/platform_view_ohos_napi.h" +#include "flutter/shell/platform/ohos/ohos_touch_processor.h" +#include "napi/native_api.h" +#include "napi_common.h" +#include "ohos_shell_holder.h" +namespace flutter { + +class XComponentBase { + private: + void BindXComponentCallback(); + void BindAccessibilityProviderCallback(); + + public: + XComponentBase(std::string id); + ~XComponentBase(); + + void AttachFlutterEngine(std::string shellholderId); + void PreDraw(std::string shellholderId, int width, int height); + void DetachFlutterEngine(); + void SetNativeXComponent(OH_NativeXComponent* nativeXComponent); + + // Callback, called by ACE XComponent + void OnSurfaceCreated(OH_NativeXComponent* component, void* window); + void OnSurfaceChanged(OH_NativeXComponent* component, void* window); + void OnSurfaceDestroyed(OH_NativeXComponent* component, void* window); + void OnDispatchTouchEvent(OH_NativeXComponent* component, void* window); + void OnDispatchMouseEvent(OH_NativeXComponent* component, void* window); + void OnDispatchMouseWheelEvent(mouseWheelEvent event); + + // Accessibility callback + int32_t FindAccessibilityNodeInfosById( + int64_t elementId, + ArkUI_AccessibilitySearchMode mode, + int32_t requestId, + ArkUI_AccessibilityElementInfoList* elementList); + int32_t FindAccessibilityNodeInfosByText( + int64_t elementId, + const char* text, + int32_t requestId, + ArkUI_AccessibilityElementInfoList* elementList); + int32_t FindFocusedAccessibilityNode( + int64_t elementId, + ArkUI_AccessibilityFocusType focusType, + int32_t requestId, + ArkUI_AccessibilityElementInfo* elementinfo); + int32_t FindNextFocusAccessibilityNode( + int64_t elementId, + ArkUI_AccessibilityFocusMoveDirection direction, + int32_t requestId, + ArkUI_AccessibilityElementInfo* elementList); + int32_t ExecuteAccessibilityAction( + int64_t elementId, + ArkUI_Accessibility_ActionType action, + ArkUI_AccessibilityActionArguments* actionArguments, + int32_t requestId); + int32_t ClearFocusedFocusAccessibilityNode(int64_t elementId); + int32_t GetAccessibilityNodeCursorPosition(int64_t elementId, + int32_t requestId, + int32_t* index); + + ArkUI_AccessibilityProvider* GetArkUIAccessibilityServiceProvider( + OH_NativeXComponent* nativeXComponent); + + OH_NativeXComponent_TouchEvent touchEvent_; + OH_NativeXComponent_Callback callback_; + OH_NativeXComponent_MouseEvent_Callback mouseCallback_; + ArkUI_AccessibilityProviderCallbacks accessibilityProviderCallback_; + std::string id_; + std::string shellholderId_; + OHOSShellHolder* shellholder_ptr_ = nullptr; + bool is_engine_attached_ = false; + bool is_surface_present_ = false; + bool is_surface_preloaded_ = false; + OH_NativeXComponent* nativeXComponent_ = nullptr; + ArkUI_AccessibilityProvider* provider_ = nullptr; + void* window_ = nullptr; + uint64_t width_ = 0; + uint64_t height_ = 0; + OhosTouchProcessor ohosTouchProcessor_; +}; + +class XComponentAdapter { + public: + XComponentAdapter(/* args */); + ~XComponentAdapter(); + static XComponentAdapter* GetInstance(); + bool Export(napi_env env, napi_value exports); + void SetNativeXComponent(std::string& id, + OH_NativeXComponent* nativeXComponent); + void AttachFlutterEngine(std::string& id, std::string& shellholderId); + void PreDraw(std::string& id, + std::string& shellholderId, + int width, + int height); + void DetachFlutterEngine(std::string& id); + void OnMouseWheel(std::string& id, mouseWheelEvent event); + XComponentBase* GetCurrentXcomponent(); + void SetCurrentXcomponentId(std::string id); + + public: + std::map xcomponetMap_; + std::mutex xcomponentMap_mutex_; + + private: + std::string current_xcomponent_id_ = ""; + static XComponentAdapter mXComponentAdapter; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_OHOS_XCOMPONENT_ADAPTER_H_ \ No newline at end of file diff --git a/shell/platform/ohos/platform_message_handler_ohos.cpp b/shell/platform/ohos/platform_message_handler_ohos.cpp new file mode 100644 index 0000000000000000000000000000000000000000..111334f721368bebc5bea4aed8d4b0f4392790f6 --- /dev/null +++ b/shell/platform/ohos/platform_message_handler_ohos.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/platform_message_handler_ohos.h" +#include "flutter/fml/make_copyable.h" + +namespace flutter { + +PlatformMessageHandlerOHOS::PlatformMessageHandlerOHOS( + const std::shared_ptr& napi_facede, + fml::RefPtr platform_task_runner) + : napi_facade_(napi_facede), + platform_task_runner_(std::move(platform_task_runner)) {} + +void PlatformMessageHandlerOHOS::HandlePlatformMessage( + std::unique_ptr message) { + int response_id = next_response_id_.fetch_add(1); + if (auto response = message->response()) { + std::lock_guard lock(pending_responses_mutex_); + pending_responses_[response_id] = response; + } + + platform_task_runner_->PostTask( + fml::MakeCopyable([response_id = response_id, napi_facede = napi_facade_, + message = std::move(message)]() mutable { + napi_facede->FlutterViewHandlePlatformMessage(response_id, + std::move(message)); + })); +} + +void PlatformMessageHandlerOHOS::InvokePlatformMessageResponseCallback( + int response_id, + std::unique_ptr mapping) { + if (!response_id) { + return; + } + // TODO(gaaclarke): Move the jump to the ui thread here from + // PlatformMessageResponseDart so we won't need to use a mutex anymore. + fml::RefPtr message_response; + { + std::lock_guard lock(pending_responses_mutex_); + auto it = pending_responses_.find(response_id); + if (it == pending_responses_.end()) { + return; + } + message_response = std::move(it->second); + pending_responses_.erase(it); + } + + message_response->Complete(std::move(mapping)); +} + +void PlatformMessageHandlerOHOS::InvokePlatformMessageEmptyResponseCallback( + int response_id) { + if (!response_id) { + return; + } + fml::RefPtr message_response; + { + std::lock_guard lock(pending_responses_mutex_); + auto it = pending_responses_.find(response_id); + if (it == pending_responses_.end()) { + return; + } + message_response = std::move(it->second); + pending_responses_.erase(it); + } + message_response->CompleteEmpty(); +} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/platform_message_handler_ohos.h b/shell/platform/ohos/platform_message_handler_ohos.h new file mode 100644 index 0000000000000000000000000000000000000000..4e1aa01300e496563e02ed8c3d0fddfc060e40fa --- /dev/null +++ b/shell/platform/ohos/platform_message_handler_ohos.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_PLATFORM_MESSAGE_HANDLER_OHOS_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_PLATFORM_MESSAGE_HANDLER_OHOS_H_ +#include +#include +#include + +#include "flutter/fml/task_runner.h" +#include "flutter/lib/ui/window/platform_message.h" +#include "flutter/shell/common/platform_message_handler.h" +#include "flutter/shell/platform/ohos/napi/platform_view_ohos_napi.h" + +namespace flutter { + +class PlatformMessageHandlerOHOS : public PlatformMessageHandler { + public: + explicit PlatformMessageHandlerOHOS( + const std::shared_ptr& napi_facede, + fml::RefPtr platform_task_runner); + void HandlePlatformMessage(std::unique_ptr message) override; + bool DoesHandlePlatformMessageOnPlatformThread() const override { + return true; + } + void InvokePlatformMessageResponseCallback( + int response_id, + std::unique_ptr mapping) override; + + void InvokePlatformMessageEmptyResponseCallback(int response_id) override; + + private: + const std::shared_ptr napi_facade_; + const fml::RefPtr platform_task_runner_; + std::atomic next_response_id_ = 1; + std::unordered_map> + pending_responses_; + std::mutex pending_responses_mutex_; +}; + +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_PLATFORM_MESSAGE_HANDLER_OHOS_H_ \ No newline at end of file diff --git a/shell/platform/ohos/platform_message_response_ohos.cpp b/shell/platform/ohos/platform_message_response_ohos.cpp new file mode 100644 index 0000000000000000000000000000000000000000..75954cd0aceffc90a03028c4963b67f23e4d33bd --- /dev/null +++ b/shell/platform/ohos/platform_message_response_ohos.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/platform_message_response_ohos.h" +#include "flutter/fml/make_copyable.h" +#include "flutter/shell/platform/ohos/napi/platform_view_ohos_napi.h" + +namespace flutter { + +PlatformMessageResponseOHOS::PlatformMessageResponseOHOS( + int response_id, + std::shared_ptr napi_facade, + fml::RefPtr platform_task_runner) + : response_id_(response_id), + napi_facade_(std::move(napi_facade)), + platform_task_runner_(std::move(platform_task_runner)) {} + +PlatformMessageResponseOHOS::~PlatformMessageResponseOHOS() = default; + +void PlatformMessageResponseOHOS::Complete(std::unique_ptr data) { + // async post + + platform_task_runner_->PostTask( + fml::MakeCopyable([response_id = response_id_, data = std::move(data), + napi_facede = napi_facade_]() mutable { + napi_facede->FlutterViewHandlePlatformMessageResponse(response_id, + std::move(data)); + })); +} + +void PlatformMessageResponseOHOS::CompleteEmpty() { + platform_task_runner_->PostTask( + fml::MakeCopyable([response_id = response_id_, // + napi_facede = napi_facade_ // + ]() { + // Make the response call into Java. + napi_facede->FlutterViewHandlePlatformMessageResponse(response_id, + nullptr); + })); +} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/platform_message_response_ohos.h b/shell/platform/ohos/platform_message_response_ohos.h new file mode 100644 index 0000000000000000000000000000000000000000..fedde2a8c52b428a23f86e6626151138e1d6b785 --- /dev/null +++ b/shell/platform/ohos/platform_message_response_ohos.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_PLATFORM_MESSAGE_RESPONSE_OHOS_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_PLATFORM_MESSAGE_RESPONSE_OHOS_H_ + +#include "flutter/fml/macros.h" +#include "flutter/fml/task_runner.h" +#include "flutter/lib/ui/window/platform_message_response.h" +#include "flutter/shell/platform/ohos/napi/platform_view_ohos_napi.h" + +#include "flutter/shell/platform/ohos/napi/platform_view_ohos_napi.h" + +namespace flutter { + +class PlatformMessageResponseOHOS : public flutter::PlatformMessageResponse { + public: + // |flutter::PlatformMessageResponse| + void Complete(std::unique_ptr data) override; + + // |flutter::PlatformMessageResponse| + void CompleteEmpty() override; + + private: + PlatformMessageResponseOHOS( + int response_id, + std::shared_ptr napi_facade, + fml::RefPtr platform_task_runner); + + ~PlatformMessageResponseOHOS() override; + + const int response_id_; + const std::shared_ptr napi_facade_; + const fml::RefPtr platform_task_runner_; + + FML_FRIEND_MAKE_REF_COUNTED(PlatformMessageResponseOHOS); + FML_DISALLOW_COPY_AND_ASSIGN(PlatformMessageResponseOHOS); +}; + +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_PLATFORM_MESSAGE_RESPONSE_OHOS_H_ \ No newline at end of file diff --git a/shell/platform/ohos/platform_view_ohos.cpp b/shell/platform/ohos/platform_view_ohos.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b6f015ff68dd87852b34991bff2b4df9f2fc54d1 --- /dev/null +++ b/shell/platform/ohos/platform_view_ohos.cpp @@ -0,0 +1,885 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/platform_view_ohos.h" +#include +#include +#include +#include +#include +#include +#include "flutter/common/constants.h" +#include "flutter/fml/make_copyable.h" +#include "flutter/impeller/renderer/backend/vulkan/context_vk.h" +#include "flutter/lib/ui/window/viewport_metrics.h" +#include "flutter/shell/common/shell_io_manager.h" +#include "flutter/shell/platform/ohos/ohos_context_gl_skia.h" +#include "flutter/shell/platform/ohos/ohos_surface_gl_skia.h" +#include "flutter/shell/platform/ohos/ohos_surface_software.h" +#include "flutter/shell/platform/ohos/platform_message_response_ohos.h" +#include "fml/trace_event.h" +#include "lib/ui/semantics/semantics_node.h" +#include "napi_common.h" +#include "ohos_context_gl_impeller.h" +#include "ohos_external_texture_gl.h" +#include "ohos_external_texture_vulkan.h" +#include "ohos_logging.h" +#include "ohos_surface_gl_impeller.h" +#include "shell/common/platform_view.h" +#include "shell/platform/ohos/accessibility/ohos_semantics_node.h" +#include "shell/platform/ohos/context/ohos_context.h" +#include "shell/platform/ohos/ohos_surface_vulkan_impeller.h" + +namespace flutter { + +// This global map's key is (texture_id) +std::map g_texture_platformview_map; +std::mutex g_map_mutex; + +OhosSurfaceFactoryImpl::OhosSurfaceFactoryImpl( + const std::shared_ptr& context) + : ohos_context_(context) {} + +OhosSurfaceFactoryImpl::~OhosSurfaceFactoryImpl() = default; + +std::unique_ptr OhosSurfaceFactoryImpl::CreateSurface() { + switch (ohos_context_->RenderingApi()) { + case OHOSRenderingAPI::kSoftware: + FML_LOG(INFO) << "OhosSurfaceFactoryImpl::CreateSurface use software"; + return std::make_unique(ohos_context_); + case OHOSRenderingAPI::kOpenGLES: + FML_LOG(INFO) << "OhosSurfaceFactoryImpl::CreateSurface use skia-gl"; + return std::make_unique(ohos_context_); + case flutter::OHOSRenderingAPI::kImpellerVulkan: + FML_LOG(INFO) + << "OhosSurfaceFactoryImpl::CreateSurface use impeller-vulkan"; + return std::make_unique(ohos_context_); + default: + FML_DCHECK(false); + return nullptr; + } +} + +std::unique_ptr CreateOHOSContext( + const flutter::TaskRunners& task_runners, + uint8_t msaa_samples, + OHOSRenderingAPI rendering_api, + bool enable_vulkan_validation, + bool enable_opengl_gpu_tracing, + bool enable_vulkan_gpu_tracing) { + TRACE_EVENT0("flutter", "CreateOHOSContext"); + switch (rendering_api) { + case OHOSRenderingAPI::kSoftware: + return std::make_unique(OHOSRenderingAPI::kSoftware); + case OHOSRenderingAPI::kOpenGLES: + return std::make_unique(OHOSRenderingAPI::kOpenGLES, + task_runners, msaa_samples); + case OHOSRenderingAPI::kImpellerVulkan: + return std::make_unique( + enable_vulkan_validation, enable_vulkan_gpu_tracing); + default: + FML_DCHECK(false); + return nullptr; + } +} + +PlatformViewOHOS::PlatformViewOHOS( + PlatformView::Delegate& delegate, + const flutter::TaskRunners& task_runners, + const std::shared_ptr& napi_facade, + bool use_software_rendering, + uint8_t msaa_samples) + : PlatformViewOHOS( + delegate, + task_runners, + napi_facade, + CreateOHOSContext( + task_runners, + msaa_samples, + delegate.OnPlatformViewGetSettings().ohos_rendering_api, + delegate.OnPlatformViewGetSettings().enable_vulkan_validation, + delegate.OnPlatformViewGetSettings().enable_opengl_gpu_tracing, + delegate.OnPlatformViewGetSettings().enable_vulkan_gpu_tracing)) { +} + +PlatformViewOHOS::PlatformViewOHOS( + PlatformView::Delegate& delegate, + const flutter::TaskRunners& task_runners, + const std::shared_ptr& napi_facade, + const std::shared_ptr& ohos_context) + : PlatformView(delegate, task_runners), + napi_facade_(napi_facade), + ohos_context_(ohos_context), + platform_message_handler_(new PlatformMessageHandlerOHOS( + napi_facade, + task_runners_.GetPlatformTaskRunner())) { + if (ohos_context_) { + FML_CHECK(ohos_context_->IsValid()) + << "Could not create surface from invalid HarmonyOS context."; + surface_factory_ = std::make_shared(ohos_context_); + ohos_surface_ = surface_factory_->CreateSurface(); + + // PrepareGpuSurface preloads the GPUSurface, which in turn preloads the + // Vulkan rendering pipeline. This helps reduce the time between application + // launch and the rendering of the first frame. The 1ms delay ensures that + // subsequent raster tasks can run first, as it can block the platform + // thread. + auto task_delay = fml::TimeDelta::FromMicroseconds(1000); + task_runners_.GetRasterTaskRunner()->PostDelayedTask( + [surface = ohos_surface_]() { surface->PrepareGpuSurface(); }, + task_delay); + FML_CHECK(ohos_surface_ && ohos_surface_->IsValid()) + << "Could not create an OpenGL, Vulkan or Software surface to set " + "up " + "rendering."; + } +} + +PlatformViewOHOS::~PlatformViewOHOS() { + FML_LOG(INFO) << "PlatformViewOHOS::~PlatformViewOHOS"; + // The UnregisterTexture cannot be called here because it depends on + // rasterizer_, and rasterizer_ may be null at this time. +} + +void PlatformViewOHOS::NotifyCreate( + fml::RefPtr native_window) { + LOGI("NotifyCreate start"); + if (ohos_surface_) { + InstallFirstFrameCallback(); + LOGI("NotifyCreate start1"); + // We register these external textures with the engine again to ensure that + // the screen is normal in the scenario of page jump and return (when there + // is a detachEngine operation during page jump, there will be a + // NotifyDestroy call, which will bring unregister texture). + for (auto [texture_id, external_texture] : all_external_texture_) { + // registerTexture must be called before PlatformView::NotifyCreated, + // because the onGrContextCreate method of the external texture will be + // called in PlatformView::NotifyCreated. + RegisterTexture(external_texture); + std::lock_guard lock(g_map_mutex); + g_texture_platformview_map[(uint64_t)texture_id] = this; + } + + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetRasterTaskRunner(), + [&, surface = ohos_surface_.get(), + native_window = std::move(native_window)]() { + LOGI("NotifyCreate start4"); + surface->SetDisplayWindow(native_window); + // Note that NotifyDestroyed will wait raster task, so platformview is + // not deleted here. + if (!window_is_preload_) { + PlatformView::NotifyCreated(); + } else if (surface->NeedNewFrame()) { + PlatformView::ScheduleFrame(); + } else { + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetPlatformTaskRunner(), + [&] { PlatformViewOHOS::FireFirstFrameCallback(); }); + } + }); + + { + std::lock_guard lock(*bridge_mutex_); + while (!semantics_queue_.empty()) { + auto semantics = semantics_queue_.front(); + semantics_queue_.pop(); + bridge_->UpdateNodeTree(semantics.first); + } + } + } +} + +void PlatformViewOHOS::Preload(int width, int height) { + if (ohos_surface_ && !window_is_preload_) { + LOGI("Preload start"); + InstallFirstFrameCallback(true); + + for (auto [texture_id, external_texture] : all_external_texture_) { + RegisterTexture(external_texture); + std::lock_guard lock(g_map_mutex); + g_texture_platformview_map[(uint64_t)texture_id] = this; + } + + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetRasterTaskRunner(), + [&, surface = ohos_surface_.get(), width, height]() { + TRACE_EVENT0("flutter", "surface:Preload"); + LOGI("Preload PlatformViewOHOS"); + if (!window_is_preload_) { + bool ret = surface->PrepareOffscreenWindow(width, height); + if (ret) { + // Note that NotifyDestroyed will wait raster task, so + // platformview is not deleted here. + PlatformView::NotifyCreated(); + window_is_preload_ = true; + } + } + }); + } +} + +void PlatformViewOHOS::NotifySurfaceWindowChanged( + fml::RefPtr native_window) { + LOGI("PlatformViewOHOS NotifySurfaceWindowChanged enter"); + TRACE_EVENT0("flutter", "NotifySurfaceWindowChanged"); + if (ohos_surface_) { + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetRasterTaskRunner(), + [&latch, width = display_width_, height = display_height_, + surface = ohos_surface_.get(), + native_window = std::move(native_window)]() { + if (native_window) { + // Reset the window size here to prevent the window size from being + // unsynchronized when the XComponent size changes. + // Note: Setting the window size in the platform thread may not take + // effect because Vulkan might request the buffer using the + // previously configured size before raster reaches this point, + // causing the window size to revert to its original value during + // the process. + native_window->SetSize(width, height); + surface->SetDisplayWindow(native_window); + } + latch.Signal(); + }); + latch.Wait(); + } +} + +void PlatformViewOHOS::NotifyChanged(const SkISize& size) { + LOGI("PlatformViewOHOS NotifyChanged enter"); + if (ohos_surface_) { + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetRasterTaskRunner(), // + [&latch, surface = ohos_surface_.get(), size]() { + surface->OnScreenSurfaceResize(size); + latch.Signal(); + }); + latch.Wait(); + } +} + +void PlatformViewOHOS::UpdateDisplaySize(int width, int height) { + if (display_width_ != width || display_height_ != height) { + display_width_ = width; + display_height_ = height; + // Here, we update the viewport to ensure that the size of the window buffer + // matches the size of the viewport. This prevents stretching or + // compression, which can occur if the physical size of the viewport differs + // from the window size. + SetViewportMetrics(kFlutterImplicitViewId, viewport_metrics_); + } +} + +// |PlatformView| +void PlatformViewOHOS::NotifyDestroyed() { + LOGI("PlatformViewOHOS NotifyDestroyed enter"); + + // Note: NotifyCreate is invoked in raster thread. So we post NotifyDestroyed + // to raster to avoid latent conflic. + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask(task_runners_.GetRasterTaskRunner(), [&]() { + window_is_preload_ = false; + // This function will internally call the GrContextDestroy of the external + // texture, and within this callback, the graphic resources occupied by the + // external texture will be released. + PlatformView::NotifyDestroyed(); + latch.Signal(); + }); + latch.Wait(); + + if (ohos_surface_) { + // If we don't remove ptr in g_texture_platformview_map, PlatformViewOHOS + // ptr in g_texture_platformview_map_ will bring use-after-free crash in + // OnNativeImageFrameAvailable. + for (const auto& [texture_id, external_texture] : all_external_texture_) { + // Here we only remove the external textures maintained internally by the + // engine, but do not actually destroy them. Without actively calling + // unregisterExternalTexture, their actual destruction will occur after + // ~PlatformViewOHOS. + UnregisterTexture(texture_id); + std::lock_guard lock(g_map_mutex); + g_texture_platformview_map.erase((uint64_t)texture_id); + } + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetRasterTaskRunner(), + [&latch, surface = ohos_surface_.get()]() { + surface->TeardownOnScreenContext(); + latch.Signal(); + }); + latch.Wait(); + } + SetSemanticsEnabled(false); +} + +void PlatformViewOHOS::SetViewportMetrics(int64_t view_id, + ViewportMetrics& metrics) { + if (display_width_ != 0 && display_height_ != 0) { + // Note: Size change notifications from ArkUI are sent tens of milliseconds + // after the window size changes. Using them for updates may cause visual + // anomalies. + // We use the previously set window size as the physical_size instead of the + // provided one to ensure that the viewport size matches the buffer size + // (avoiding screen stretching). As a result, size updates from the ArkUI + // layer will not take effect. + metrics.physical_width = display_width_; + metrics.physical_height = display_height_; + } + FML_LOG(INFO) << "SetViewportMetrics physical size: " + << metrics.physical_width << "," << metrics.physical_height + << " display size: " << display_width_ << "," + << display_height_; + viewport_metrics_ = metrics; + PlatformView::SetViewportMetrics(view_id, metrics); +} + +// todo + +void PlatformViewOHOS::DispatchPlatformMessage(std::string name, + void* message, + int messageLenth, + int reponseId) { + FML_DLOG(INFO) << "DispatchPlatformMessage(" << name << "," << messageLenth + << "," << reponseId; + fml::MallocMapping mapMessage = + fml::MallocMapping::Copy(message, messageLenth); + + fml::RefPtr response; + response = fml::MakeRefCounted( + reponseId, napi_facade_, task_runners_.GetPlatformTaskRunner()); + + PlatformView::DispatchPlatformMessage( + std::make_unique( + std::move(name), std::move(mapMessage), std::move(response))); +} + +void PlatformViewOHOS::DispatchEmptyPlatformMessage(std::string name, + int reponseId) { + FML_DLOG(INFO) << "DispatchEmptyPlatformMessage (" << name << "," + << reponseId; + fml::RefPtr response; + response = fml::MakeRefCounted( + reponseId, napi_facade_, task_runners_.GetPlatformTaskRunner()); + + PlatformView::DispatchPlatformMessage( + std::make_unique(std::move(name), + std::move(response))); +} + +// |PlatformView| +void PlatformViewOHOS::LoadDartDeferredLibrary( + intptr_t loading_unit_id, + std::unique_ptr snapshot_data, + std::unique_ptr snapshot_instructions) { + FML_DLOG(INFO) << "LoadDartDeferredLibrary:" << loading_unit_id; + delegate_.LoadDartDeferredLibrary(loading_unit_id, std::move(snapshot_data), + std::move(snapshot_instructions)); +} + +void PlatformViewOHOS::LoadDartDeferredLibraryError( + intptr_t loading_unit_id, + const std::string error_message, + bool transient) { + FML_DLOG(INFO) << "LoadDartDeferredLibraryError:" << loading_unit_id << ":" + << error_message; + delegate_.LoadDartDeferredLibraryError(loading_unit_id, error_message, + transient); +} + +// |PlatformView| +void PlatformViewOHOS::UpdateAssetResolverByType( + std::unique_ptr updated_asset_resolver, + AssetResolver::AssetResolverType type) { + FML_DLOG(INFO) << "UpdateAssetResolverByType"; + delegate_.UpdateAssetResolverByType(std::move(updated_asset_resolver), type); +} + +// ohos_accessbility_bridge +void PlatformViewOHOS::UpdateSemantics( + flutter::SemanticsNodeUpdates update, + flutter::CustomAccessibilityActionUpdates actions) { + TRACE_EVENT0("flutter", "UpdateSemantics"); + if (bridge_->provider_ohos_ == nullptr) { + semantics_queue_.push(std::make_pair(update, actions)); + FML_DLOG(INFO) << "PlatformViewOHOS::UpdateSemantics is called when " + "bridge_.provider_ohos_ is nullptr "; + return; + } else if (!semantics_queue_.empty()) { + FML_DLOG(WARNING) + << "PlatformViewOHOS::UpdateSemantics has unhandled calls"; + } + std::lock_guard lock(*bridge_mutex_); + bridge_->UpdateNodeTree(update); +} + +// |PlatformView| +void PlatformViewOHOS::HandlePlatformMessage( + std::unique_ptr message) { + FML_DLOG(INFO) << "HandlePlatformMessage"; + platform_message_handler_->HandlePlatformMessage(std::move(message)); +} + +// |PlatformView| +void PlatformViewOHOS::OnPreEngineRestart() const { + FML_DLOG(INFO) << "OnPreEngineRestart"; + task_runners_.GetPlatformTaskRunner()->PostTask( + fml::MakeCopyable([napi_facede = napi_facade_]() mutable { + napi_facede->FlutterViewOnPreEngineRestart(); + })); +} + +// |PlatformView| +std::unique_ptr PlatformViewOHOS::CreateVSyncWaiter() { + FML_DLOG(INFO) << "CreateVSyncWaiter"; + return std::make_unique(task_runners_, enable_frame_cache_); +} + +// |PlatformView| +std::unique_ptr PlatformViewOHOS::CreateRenderingSurface() { + FML_DLOG(INFO) << "CreateRenderingSurface"; + if (ohos_surface_ == nullptr) { + FML_DLOG(ERROR) << "CreateRenderingSurface Failed.ohos_surface_ is null "; + return nullptr; + } + + LOGD("return CreateGPUSurface"); + return ohos_surface_->CreateGPUSurface( + ohos_context_->GetMainSkiaContext().get()); +} + +// |PlatformView| +std::shared_ptr +PlatformViewOHOS::CreateExternalViewEmbedder() { + FML_DLOG(INFO) << "CreateExternalViewEmbedder"; + return nullptr; +} + +// |PlatformView| +std::unique_ptr +PlatformViewOHOS::CreateSnapshotSurfaceProducer() { + FML_DLOG(INFO) << "CreateSnapshotSurfaceProducer"; + return std::make_unique(*(ohos_surface_.get())); +} + +// |PlatformView| +sk_sp PlatformViewOHOS::CreateResourceContext() const { + FML_DLOG(INFO) << "CreateResourceContext"; + if (!ohos_surface_) { + return nullptr; + } + sk_sp resource_context; + if (ohos_surface_->ResourceContextMakeCurrent()) { + // TODO(chinmaygarde): Currently, this code depends on the fact that only + // the OpenGL surface will be able to make a resource context current. If + // this changes, this assumption breaks. Handle the same. + resource_context = ShellIOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, + GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); + } else { + FML_DLOG(ERROR) << "Could not make the resource context current."; + } + + return resource_context; +} + +// |PlatformView| +void PlatformViewOHOS::ReleaseResourceContext() const { + LOGI("PlatformViewOHOS::ReleaseResourceContext"); + // IO thread will invoke glGetError() when exit. + // It will bring lots of "Call To OpenGL ES API With No Current Context" + // without gl context. So we don't clear current. + // if (ohos_surface_) { + // ohos_surface_->ResourceContextClearCurrent(); + // } +} + +// |PlatformView| +std::shared_ptr PlatformViewOHOS::GetImpellerContext() + const { + FML_DLOG(INFO) << "GetImpellerContext"; + if (ohos_surface_) { + return ohos_surface_->GetImpellerContext(); + } + return nullptr; +} + +// |PlatformView| +std::unique_ptr> +PlatformViewOHOS::ComputePlatformResolvedLocales( + const std::vector& supported_locale_data) { + FML_DLOG(INFO) << "ComputePlatformResolvedLocales"; + return napi_facade_->FlutterViewComputePlatformResolvedLocales( + supported_locale_data); +} + +// |PlatformView| +void PlatformViewOHOS::RequestDartDeferredLibrary(intptr_t loading_unit_id) { + FML_DLOG(INFO) << "RequestDartDeferredLibrary:" << loading_unit_id; + return; +} + +void PlatformViewOHOS::InstallFirstFrameCallback(bool is_preload) { + FML_DLOG(INFO) << "InstallFirstFrameCallback"; + SetNextFrameCallback( + [platform_view = GetWeakPtr(), + platform_task_runner = task_runners_.GetPlatformTaskRunner(), + is_preload]() { + platform_task_runner->PostTask([platform_view, is_preload]() { + // Back on Platform Task Runner. + FML_DLOG(INFO) << "install InstallFirstFrameCallback "; + if (platform_view) { + reinterpret_cast(platform_view.get()) + ->FireFirstFrameCallback(is_preload); + } + }); + }); +} + +void PlatformViewOHOS::FireFirstFrameCallback(bool is_preload) { + FML_DLOG(INFO) << "FlutterViewOnFirstFrame"; + napi_facade_->FlutterViewOnFirstFrame(is_preload); +} + +PointerDataDispatcherMaker PlatformViewOHOS::GetDispatcherMaker() { + return [](DefaultPointerDataDispatcher::Delegate& delegate) { + return std::make_unique(delegate); + }; +} + +std::shared_ptr PlatformViewOHOS::CreateExternalTexture( + int64_t texture_id) { + uint64_t context_frame_data = (uint64_t)texture_id; + OH_OnFrameAvailableListener listener; + listener.context = (void*)context_frame_data; + listener.onFrameAvailable = &PlatformViewOHOS::OnNativeImageFrameAvailable; + std::shared_ptr extrenal_texture = nullptr; + FML_LOG(INFO) << " RegisterExternalTexture api type " + << int(ohos_context_->RenderingApi()) << " texture_id " + << texture_id; + if (ohos_context_->RenderingApi() == OHOSRenderingAPI::kOpenGLES) { + extrenal_texture = + std::make_shared(texture_id, listener); + } else if (ohos_context_->RenderingApi() == + OHOSRenderingAPI::kImpellerVulkan) { + extrenal_texture = std::make_shared( + std::static_pointer_cast( + ohos_context_->GetImpellerContext()), + texture_id, listener); + } + if (extrenal_texture && extrenal_texture->GetProducerSurfaceId() != 0 && + extrenal_texture->GetProducerWindowId() != 0) { + std::lock_guard lock(g_map_mutex); + g_texture_platformview_map[context_frame_data] = this; + all_external_texture_[texture_id] = extrenal_texture; + RegisterTexture(extrenal_texture); + } + return extrenal_texture; +} + +uint64_t PlatformViewOHOS::RegisterExternalTexture(int64_t texture_id) { + auto extrenal_texture = CreateExternalTexture(texture_id); + if (extrenal_texture == nullptr) { + return 0; + } else { + return extrenal_texture->GetProducerSurfaceId(); + } + return 0; +} + +uint64_t PlatformViewOHOS::GetExternalTextureWindowId(int64_t texture_id) { + if (all_external_texture_.find(texture_id) != all_external_texture_.end()) { + auto external_texture = all_external_texture_[texture_id]; + return external_texture->GetProducerWindowId(); + } + return 0; +} + +void PlatformViewOHOS::OnNativeImageFrameAvailable(void* data) { + uint64_t ptexture_id = (uint64_t)data; + std::lock_guard lock(g_map_mutex); + if (g_texture_platformview_map.find(ptexture_id) == + g_texture_platformview_map.end()) { + return; + } + PlatformViewOHOS* platform = g_texture_platformview_map[ptexture_id]; + + if (platform == nullptr || platform->ohos_surface_ == nullptr) { + FML_LOG(ERROR) << "OnNativeImageFrameAvailable NotifyDstroyed, will not " + "MarkTextureFrameAvailable"; + return; + } + + // Note: RunNowOrPostTask may get dead lock when running in platform thread. + platform->task_runners_.GetPlatformTaskRunner()->PostTask([ptexture_id]() { + std::lock_guard lock(g_map_mutex); + if (g_texture_platformview_map.find(ptexture_id) == + g_texture_platformview_map.end()) { + return; + } + PlatformViewOHOS* platform = g_texture_platformview_map[ptexture_id]; + uint64_t texture_id = ptexture_id; + platform->MarkTextureFrameAvailable(texture_id); + }); +} + +void PlatformViewOHOS::UnRegisterExternalTexture(int64_t texture_id) { + all_external_texture_.erase(texture_id); + FML_LOG(INFO) << "UnRegisterExternalTexture " << texture_id; + // Note that external_texture will be destroy after UnregisterTexture. + UnregisterTexture(texture_id); + + // Wait to prevent potential conflicts with SetExternalNativeImage(use same + // NativeImage) being called from another raster thread. + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask(task_runners_.GetRasterTaskRunner(), + [&latch]() { latch.Signal(); }); + latch.Wait(); + + std::lock_guard lock(g_map_mutex); + g_texture_platformview_map.erase((uint64_t)texture_id); +} + +void PlatformViewOHOS::RegisterExternalTextureByPixelMap( + int64_t texture_id, + NativePixelMap* pixelMap, + OH_NativeBuffer* pixelMap_native_buffer) { + auto extrenal_texture = CreateExternalTexture(texture_id); + if (extrenal_texture != nullptr) { + extrenal_texture->SetPixelMapAsProducer(pixelMap, pixelMap_native_buffer); + } +} + +void PlatformViewOHOS::SetExternalTextureBackGroundPixelMap( + int64_t texture_id, + NativePixelMap* pixelMap, + OH_NativeBuffer* pixelMap_native_buffer) { + if (all_external_texture_.find(texture_id) != all_external_texture_.end()) { + auto external_texture = all_external_texture_[texture_id]; + FML_LOG(INFO) << "SetExternalTextureBackGroundPixelMap " << texture_id; + external_texture->SetPixelMapAsProducer(pixelMap, pixelMap_native_buffer); + } +} + +void PlatformViewOHOS::SetTextureBufferSize(int64_t texture_id, + int32_t width, + int32_t height) { + if (all_external_texture_.find(texture_id) != all_external_texture_.end()) { + auto external_texture = all_external_texture_[texture_id]; + external_texture->SetProducerWindowSize(width, height); + } +} + +void PlatformViewOHOS::NotifyTextureResizing(int64_t texture_id, + int32_t width, + int32_t height) { + if (all_external_texture_.find(texture_id) != all_external_texture_.end()) { + auto external_texture = all_external_texture_[texture_id]; + external_texture->NotifyResizing(width, height); + } +} + +bool PlatformViewOHOS::SetExternalNativeImage(int64_t texture_id, + OH_NativeImage* native_image) { + if (all_external_texture_.find(texture_id) != all_external_texture_.end()) { + auto external_texture = all_external_texture_[texture_id]; + fml::AutoResetWaitableEvent latch; + bool result = false; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetRasterTaskRunner(), + [&external_texture, &latch, &result, native_image]() { + result = external_texture->SetExternalNativeImage(native_image); + latch.Signal(); + }); + latch.Wait(); + return result; + } else { + return false; + } +} + +uint64_t PlatformViewOHOS::ResetExternalTexture(int64_t texture_id, + bool need_surfaceId) { + if (all_external_texture_.find(texture_id) != all_external_texture_.end()) { + FML_LOG(INFO) << "ResetExternalTexture " << texture_id; + + auto external_texture = all_external_texture_[texture_id]; + fml::AutoResetWaitableEvent latch; + uint64_t surface_id = 0; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetRasterTaskRunner(), + [&external_texture, &latch, &surface_id, need_surfaceId]() { + surface_id = external_texture->Reset(need_surfaceId); + latch.Signal(); + }); + latch.Wait(); + return surface_id; + } else { + return 0; + } +} + +void PlatformViewOHOS::OnTouchEvent( + const std::shared_ptr touchPacketString, + int size) { + return napi_facade_->FlutterViewOnTouchEvent(touchPacketString, size); +} + +void PlatformViewOHOS::RunTask(OhosThreadType type, const fml::closure& task) { + fml::RefPtr TaskRunnerPtr = nullptr; + switch (type) { + case OhosThreadType::kPlatform: + TaskRunnerPtr = task_runners_.GetPlatformTaskRunner(); + break; + case OhosThreadType::kUI: + TaskRunnerPtr = task_runners_.GetUITaskRunner(); + break; + case OhosThreadType::kRaster: + TaskRunnerPtr = task_runners_.GetRasterTaskRunner(); + break; + case OhosThreadType::kIO: + TaskRunnerPtr = task_runners_.GetIOTaskRunner(); + break; + default: + break; + } + + if (!TaskRunnerPtr) { + return; + } + + fml::TaskRunner::RunNowOrPostTask(TaskRunnerPtr, task); +} + +void PlatformViewOHOS::SetSemanticsBridge( + std::shared_ptr bridge, + std::shared_ptr mutex) { + bridge_ = std::move(bridge); + bridge_mutex_ = std::move(mutex); +} + +void PlatformViewOHOS::AccessibilityAnnounce(std::unique_ptr& message) { + std::lock_guard lock(*bridge_mutex_); + bridge_->Announce(message); +} + +void PlatformViewOHOS::AccessibilityOnTap(int32_t nodeId) { + std::lock_guard lock(*bridge_mutex_); + bridge_->OnTap(nodeId); +} + +void PlatformViewOHOS::AccessibilityOnLongPress(int32_t nodeId) { + std::lock_guard lock(*bridge_mutex_); + bridge_->OnLongPress(nodeId); +} + +void PlatformViewOHOS::AccessibilityOnTooltip( + std::unique_ptr& message) { + std::lock_guard lock(*bridge_mutex_); + bridge_->OnTooltip(message); +} + +void PlatformViewOHOS::OnAccessibilityStateChange(bool state) { + if (state) { + SetSemanticsEnabled(true); + std::lock_guard lock(*bridge_mutex_); + bridge_->OnAccessibilityStateChange(state); + } else { + SetAccessibleNavigation(false); + SetSemanticsEnabled(false); + } +} + +void PlatformViewOHOS::SetAccessibleNavigation(bool isAccessibleNavigation) { + std::lock_guard lock(*bridge_mutex_); + bridge_->OnAccessibilityNavigation(isAccessibleNavigation); + + if (is_accessibility_navigation_ == isAccessibleNavigation) { + return; + } + is_accessibility_navigation_ = isAccessibleNavigation; + if (is_accessibility_navigation_) { + accessibility_feature_flags_ |= + static_cast(AccessibilityFeatureFlag::kAccessibleNavigation); + FML_DLOG(INFO) << "SetAccessibleNavigation -> accessibilityFeatureFlags: " + << accessibility_feature_flags_; + } else { + accessibility_feature_flags_ &= + ~static_cast(AccessibilityFeatureFlag::kAccessibleNavigation); + } + SetAccessibilityFeatures(accessibility_feature_flags_); +} + +void PlatformViewOHOS::SetBoldText(double fontWeightScale) { + bool shouldBold = fontWeightScale > 1.0; + if (shouldBold) { + accessibility_feature_flags_ |= + static_cast(AccessibilityFeatureFlag::kBoldText); + FML_DLOG(INFO) << "SetBoldText -> accessibilityFeatureFlags: " + << accessibility_feature_flags_; + } else { + accessibility_feature_flags_ &= + static_cast(AccessibilityFeatureFlag::kBoldText); + } + SetAccessibilityFeatures(accessibility_feature_flags_); +} + +void PlatformViewOHOS::SimulateTouchEvent(SemanticsNodeExtend* node) { + const int numTouchPoints = 1; + const float simulatePressure = 0.05; + PointerData pointerData; + + pointerData.Clear(); + pointerData.embedder_id = 0; + pointerData.change = PointerData::Change::kDown; + pointerData.physical_y = + (node->absoluteRect.fTop + node->absoluteRect.fBottom) / 2; + pointerData.physical_x = + (node->absoluteRect.fLeft + node->absoluteRect.fRight) / 2; + pointerData.physical_delta_x = 0.0; + pointerData.physical_delta_y = 0.0; + pointerData.device = 0; + pointerData.pointer_identifier = 0; + pointerData.signal_kind = PointerData::SignalKind::kNone; + pointerData.scroll_delta_x = 0.0; + pointerData.scroll_delta_y = 0.0; + pointerData.pressure = simulatePressure; + pointerData.pressure_max = 1.0; + pointerData.pressure_min = 0.0; + pointerData.kind = PointerData::DeviceKind::kTouch; + pointerData.buttons = kPointerButtonTouchContact; + pointerData.pan_x = 0.0; + pointerData.pan_y = 0.0; + pointerData.pan_delta_x = 0.0; + pointerData.pan_delta_y = 0.0; + pointerData.size = 0; + pointerData.scale = 1.0; + pointerData.rotation = 0.0; + + std::unique_ptr downPacket = + std::make_unique(numTouchPoints); + downPacket->SetPointerData(0, pointerData); + DispatchPointerDataPacket(std::move(downPacket)); + std::unique_ptr upPacket = + std::make_unique(numTouchPoints); + pointerData.change = PointerData::Change::kUp; + pointerData.buttons = 0; + upPacket->SetPointerData(0, pointerData); + DispatchPointerDataPacket(std::move(upPacket)); +} + +} // namespace flutter diff --git a/shell/platform/ohos/platform_view_ohos.h b/shell/platform/ohos/platform_view_ohos.h new file mode 100644 index 0000000000000000000000000000000000000000..8eb23989e8136a2244ebb33ace4802fe14470f57 --- /dev/null +++ b/shell/platform/ohos/platform_view_ohos.h @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_PLATFORM_VIEW_OHOS_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_PLATFORM_VIEW_OHOS_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/lib/ui/window/platform_message.h" +#include "flutter/shell/common/platform_view.h" +#include "flutter/shell/platform/ohos/accessibility/ohos_semantics_bridge.h" +#include "flutter/shell/platform/ohos/context/ohos_context.h" +#include "flutter/shell/platform/ohos/napi/platform_view_ohos_napi.h" +#include "flutter/shell/platform/ohos/ohos_external_texture_gl.h" +#include "flutter/shell/platform/ohos/platform_message_handler_ohos.h" +#include "flutter/shell/platform/ohos/surface/ohos_native_window.h" +#include "flutter/shell/platform/ohos/surface/ohos_snapshot_surface_producer.h" +#include "flutter/shell/platform/ohos/surface/ohos_surface.h" +#include "flutter/shell/platform/ohos/vsync_waiter_ohos.h" + +namespace flutter { + +enum class OhosThreadType { + kPlatform, + kUI, + kRaster, + kIO, +}; + +class OhosSurfaceFactoryImpl : public OhosSurfaceFactory { + public: + OhosSurfaceFactoryImpl(const std::shared_ptr& context); + + ~OhosSurfaceFactoryImpl() override; + + std::unique_ptr CreateSurface() override; + + private: + const std::shared_ptr& ohos_context_; +}; + +class PlatformViewOHOS final : public PlatformView { + public: + PlatformViewOHOS(PlatformView::Delegate& delegate, + const flutter::TaskRunners& task_runners, + const std::shared_ptr& napi_facade, + bool use_software_rendering, + uint8_t msaa_samples); + + PlatformViewOHOS(PlatformView::Delegate& delegate, + const flutter::TaskRunners& task_runners, + const std::shared_ptr& napi_facade, + const std::shared_ptr& OHOS_context); + + ~PlatformViewOHOS() override; + + void NotifyCreate(fml::RefPtr native_window); + + void Preload(int width, int height); + + void NotifySurfaceWindowChanged(fml::RefPtr native_window); + + void NotifyChanged(const SkISize& size); + + /** + * @brief Update the size of the current Flutter window. This function will + * also synchronize the viewport size. + * + * @param width + * @param height + */ + void UpdateDisplaySize(int width, int height); + + // |PlatformView| + void NotifyDestroyed() override; + + void SetViewportMetrics(int64_t view_id, ViewportMetrics& metrics); + + // todo + void DispatchPlatformMessage(std::string name, + void* message, + int messageLenth, + int reponseId); + + void DispatchEmptyPlatformMessage(std::string name, int reponseId); + + std::shared_ptr CreateExternalTexture( + int64_t texture_id); + + uint64_t RegisterExternalTexture(int64_t texture_id); + + void RegisterExternalTextureByPixelMap( + int64_t texture_id, + NativePixelMap* pixelMap, + OH_NativeBuffer* pixelMap_native_buffer); + + void SetExternalTextureBackGroundPixelMap( + int64_t texture_id, + NativePixelMap* pixelMap, + OH_NativeBuffer* pixelMap_native_buffer); + + void SetTextureBufferSize(int64_t texture_id, int32_t width, int32_t height); + + void NotifyTextureResizing(int64_t texture_id, int32_t width, int32_t height); + + bool SetExternalNativeImage(int64_t texture_id, OH_NativeImage* native_image); + + void UnRegisterExternalTexture(int64_t texture_id); + + uint64_t GetExternalTextureWindowId(int64_t texture_id); + + uint64_t ResetExternalTexture(int64_t texture_id, bool need_surfaceId); + + void EnableFrameCache(bool enable) { *enable_frame_cache_ = enable; }; + + // |PlatformView| + PointerDataDispatcherMaker GetDispatcherMaker() override; + + // |PlatformView| + void LoadDartDeferredLibrary( + intptr_t loading_unit_id, + std::unique_ptr snapshot_data, + std::unique_ptr snapshot_instructions) override; + + void LoadDartDeferredLibraryError(intptr_t loading_unit_id, + const std::string error_message, + bool transient) override; + + // |PlatformView| + void UpdateAssetResolverByType( + std::unique_ptr updated_asset_resolver, + AssetResolver::AssetResolverType type) override; + + const std::shared_ptr& GetOHOSContext() { return ohos_context_; } + + std::shared_ptr GetPlatformMessageHandler() + const override { + return platform_message_handler_; + } + + void OnTouchEvent(std::shared_ptr touchPacketString, int size); + + void RunTask(OhosThreadType type, const fml::closure& task); + + void SetSemanticsBridge(std::shared_ptr bridge, + std::shared_ptr mutex); + void AccessibilityAnnounce(std::unique_ptr& message); + void AccessibilityOnTap(int32_t nodeId); + void AccessibilityOnLongPress(int32_t nodeId); + void AccessibilityOnTooltip(std::unique_ptr& message); + void OnAccessibilityStateChange(bool state); + void SetAccessibleNavigation(bool isAccessibleNavigation); + void SetBoldText(double fontWeightScale); + + void SimulateTouchEvent(SemanticsNodeExtend* node); + + private: + const std::shared_ptr napi_facade_; + std::shared_ptr ohos_context_; + + std::shared_ptr ohos_surface_; + std::shared_ptr platform_message_handler_; + + std::shared_ptr surface_factory_; + std::map> all_external_texture_; + + std::shared_ptr enable_frame_cache_ = std::make_shared(true); + + // viewport will use this size + int display_width_ = 0; + int display_height_ = 0; + + ViewportMetrics viewport_metrics_; + + bool window_is_preload_ = false; + + // accessibility + std::queue> + semantics_queue_; + + std::shared_ptr bridge_; + std::shared_ptr bridge_mutex_; + int32_t accessibility_feature_flags_ = 0; + bool is_accessibility_navigation_ = false; + + // |PlatformView| + void UpdateSemantics( + flutter::SemanticsNodeUpdates update, + flutter::CustomAccessibilityActionUpdates actions) override; + + // |PlatformView| + void HandlePlatformMessage( + std::unique_ptr message) override; + + // |PlatformView| + void OnPreEngineRestart() const override; + + // |PlatformView| + std::unique_ptr CreateVSyncWaiter() override; + + // |PlatformView| + std::unique_ptr CreateRenderingSurface() override; + + // |PlatformView| + std::shared_ptr CreateExternalViewEmbedder() override; + + // |PlatformView| + std::unique_ptr CreateSnapshotSurfaceProducer() + override; + + // |PlatformView| + sk_sp CreateResourceContext() const override; + + // |PlatformView| + void ReleaseResourceContext() const override; + + // |PlatformView| + std::shared_ptr GetImpellerContext() const override; + + // |PlatformView| + std::unique_ptr> ComputePlatformResolvedLocales( + const std::vector& supported_locale_data) override; + + // |PlatformView| + void RequestDartDeferredLibrary(intptr_t loading_unit_id) override; + + void InstallFirstFrameCallback(bool is_preload = false); + + void FireFirstFrameCallback(bool is_preload = false); + + FML_DISALLOW_COPY_AND_ASSIGN(PlatformViewOHOS); + + static void OnNativeImageFrameAvailable(void* data); +}; + +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_PLATFORM_VIEW_OHOS_H_ \ No newline at end of file diff --git a/shell/platform/ohos/surface/ohos_native_window.cpp b/shell/platform/ohos/surface/ohos_native_window.cpp new file mode 100644 index 0000000000000000000000000000000000000000..afb9ca83c8716f420202e9d3292ffdba5c282eb2 --- /dev/null +++ b/shell/platform/ohos/surface/ohos_native_window.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/surface/ohos_native_window.h" +#include "flutter/fml/logging.h" + +namespace flutter { + +OHOSNativeWindow::OHOSNativeWindow(Handle window) + : window_(window), is_fake_window_(false) { + FML_LOG(INFO) << " native_window:" << (int64_t)window_; +} + +OHOSNativeWindow::OHOSNativeWindow(Handle window, bool is_fake_window) + : window_(window), is_fake_window_(is_fake_window) {} + +OHOSNativeWindow::~OHOSNativeWindow() { + if (window_ != nullptr) { + window_ = nullptr; + } +} + +bool OHOSNativeWindow::IsValid() const { + return window_ != nullptr; +} + +SkISize OHOSNativeWindow::GetSize() const { + if (window_ != nullptr) { + int32_t width, height; + int ret = OH_NativeWindow_NativeWindowHandleOpt( + window_, GET_BUFFER_GEOMETRY, &height, &width); + if (ret != 0) { + FML_LOG(ERROR) << "OH_NativeWindow_NativeWindowHandleOpt GetSize err:" + << ret; + return SkISize::Make(0, 0); + } + return SkISize::Make(width, height); + } + return SkISize::Make(0, 0); +} + +void OHOSNativeWindow::SetSize(int width, int height) { + if (window_ != nullptr) { + int ret = OH_NativeWindow_NativeWindowHandleOpt( + window_, SET_BUFFER_GEOMETRY, width, height); + if (ret != 0) { + FML_LOG(ERROR) << "OHOSNativeWindow setSize failed:" << ret; + } + } + return; +} + +OHOSNativeWindow::Handle OHOSNativeWindow::Gethandle() const { + return window_; +} + +OHOSNativeWindow::Handle OHOSNativeWindow::handle() const { + return window_; +} + +} // namespace flutter diff --git a/shell/platform/ohos/surface/ohos_native_window.h b/shell/platform/ohos/surface/ohos_native_window.h new file mode 100644 index 0000000000000000000000000000000000000000..4b2e4451438a4400b331069aef517fa5f0efcc63 --- /dev/null +++ b/shell/platform/ohos/surface/ohos_native_window.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_SURFACE_OHOS_NATIVE_WINDOW_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_SURFACE_OHOS_NATIVE_WINDOW_H_ + +#include "flutter/fml/macros.h" +#include "flutter/fml/memory/ref_counted.h" +#include "third_party/skia/include/core/SkSize.h" + +#include + +namespace flutter { + +/* + class adapater for ThreadSafe +*/ +class OHOSNativeWindow : public fml::RefCountedThreadSafe { + public: + using Handle = OHNativeWindow*; + + Handle Gethandle() const; + + bool IsValid() const; + + SkISize GetSize() const; + + void SetSize(int width, int height); + + Handle handle() const; + + /// Returns true when this HarmonyOS NativeWindow is not backed by a real + /// window (used for testing). + bool IsFakeWindow() const { return is_fake_window_; } + + private: + Handle window_; + const bool is_fake_window_; + + explicit OHOSNativeWindow(Handle window); + explicit OHOSNativeWindow(Handle window, bool is_fake_window); + + ~OHOSNativeWindow(); + + FML_FRIEND_MAKE_REF_COUNTED(OHOSNativeWindow); + FML_FRIEND_REF_COUNTED_THREAD_SAFE(OHOSNativeWindow); + FML_DISALLOW_COPY_AND_ASSIGN(OHOSNativeWindow); +}; +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_SURFACE_OHOS_NATIVE_WINDOW_H_ diff --git a/shell/platform/ohos/surface/ohos_snapshot_surface_producer.cpp b/shell/platform/ohos/surface/ohos_snapshot_surface_producer.cpp new file mode 100755 index 0000000000000000000000000000000000000000..21d0d333f3063a4761cdf3f9b4ad6a506102788b --- /dev/null +++ b/shell/platform/ohos/surface/ohos_snapshot_surface_producer.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/surface/ohos_snapshot_surface_producer.h" + +namespace flutter { + +OHOSSnapshotSurfaceProducer::OHOSSnapshotSurfaceProducer( + OHOSSurface& ohos_surface) + : ohos_surface_(ohos_surface) { + FML_DLOG(WARNING) << "Flutter OHOSSnapshotSurfaceProducer()."; +} + +// |SnapshotSurfaceProducer| +std::unique_ptr OHOSSnapshotSurfaceProducer::CreateSnapshotSurface() { + FML_DLOG(WARNING) << "Flutter CreateSnapshotSurface()."; + return ohos_surface_.CreateSnapshotSurface(); +} + +} // namespace flutter diff --git a/shell/platform/ohos/surface/ohos_snapshot_surface_producer.h b/shell/platform/ohos/surface/ohos_snapshot_surface_producer.h new file mode 100755 index 0000000000000000000000000000000000000000..ba890b03587156759c0e33dd3436aaf0701e5db9 --- /dev/null +++ b/shell/platform/ohos/surface/ohos_snapshot_surface_producer.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 OHOS_SNAPSHOT_SURFACE_PRODUCER_H +#define OHOS_SNAPSHOT_SURFACE_PRODUCER_H +#include "flutter/flow/surface.h" +#include "flutter/shell/common/snapshot_surface_producer.h" +#include "flutter/shell/platform/ohos/surface/ohos_surface.h" + +namespace flutter { +class OHOSSnapshotSurfaceProducer : public SnapshotSurfaceProducer { + public: + explicit OHOSSnapshotSurfaceProducer(OHOSSurface& ohos_surface); + + // |SnapshotSurfaceProducer| + std::unique_ptr CreateSnapshotSurface() override; + + private: + OHOSSurface& ohos_surface_; +}; +} // namespace flutter + +#endif \ No newline at end of file diff --git a/shell/platform/ohos/surface/ohos_surface.cpp b/shell/platform/ohos/surface/ohos_surface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..464740807013738811c3f35abe1ed0e0a5976fea --- /dev/null +++ b/shell/platform/ohos/surface/ohos_surface.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/surface/ohos_surface.h" +#include +#include +#include +#include "fml/trace_event.h" +namespace flutter { + +std::map g_surface_is_alive; +std::mutex g_surface_alive_mutex; + +OHOSSurface::OHOSSurface(const std::shared_ptr& ohos_context) + : ohos_context_(ohos_context) { + FML_DCHECK(ohos_context->IsValid()); + ohos_context_ = ohos_context; +} + +std::unique_ptr OHOSSurface::CreateSnapshotSurface() { + return nullptr; +} + +OHOSSurface::~OHOSSurface() { + std::lock_guard lock(g_surface_alive_mutex); + g_surface_is_alive.erase((uint64_t)this); + ReleaseOffscreenWindow(); +} + +std::shared_ptr OHOSSurface::GetImpellerContext() { + return nullptr; +} + +bool OHOSSurface::PrepareOffscreenWindow(int32_t width, int32_t height) { + TRACE_EVENT0("flutter", "OHOSSurface-PrepareContext"); + + if (offscreen_native_image_ != nullptr && offscreen_height_ == height && + offscreen_width_ == width) { + return true; + } + + offscreen_native_image_ = OH_NativeImage_Create(0, 0); + + offscreen_height_ = height; + offscreen_width_ = width; + + offscreen_nativewindow_ = + OH_NativeImage_AcquireNativeWindow(offscreen_native_image_); + if (offscreen_nativewindow_ == nullptr) { + FML_LOG(ERROR) << "offscreen OH_NativeImage_AcquireNativeWindow get null"; + return false; + } + + int ret = OH_NativeWindow_NativeWindowHandleOpt( + offscreen_nativewindow_, SET_BUFFER_GEOMETRY, width, height); + if (ret != 0) { + FML_LOG(ERROR) << "offscreen OH_NativeWindow_NativeWindowHandleOpt " + "set_buffer_size err:" + << ret; + return false; + } + FML_LOG(INFO) << "set offscreen window" << offscreen_nativewindow_; + + SetNativeWindow(fml::MakeRefCounted( + static_cast(offscreen_nativewindow_))); + + OH_OnFrameAvailableListener listener; + std::lock_guard lock(g_surface_alive_mutex); + listener.context = (void*)this; + listener.onFrameAvailable = &OHOSSurface::OnFrameAvailable; + g_surface_is_alive[(uint64_t)this] = true; + ret = OH_NativeImage_SetOnFrameAvailableListener(offscreen_native_image_, + listener); + if (ret != 0) { + FML_LOG(ERROR) << "offscreen SetOnFrameAvailableListener err:" << ret; + } + + return true; +} + +void OHOSSurface::ReleaseOffscreenWindow() { + if (last_nativewindow_buffer_) { + FML_LOG(INFO) << "release last_nativewindow_buffer_ " + << last_nativewindow_buffer_; + + // OH_NativeImage_ReleaseNativeWindowBuffer will close the fence_fd even if + // it fails. + int ret = OH_NativeImage_ReleaseNativeWindowBuffer( + offscreen_native_image_, last_nativewindow_buffer_, last_fence_fd_); + if (ret != 0) { + // Swapchain destroying may clean the buffercache of + // offscreen_native_image_. In this situation, we need destroy the buffer. + FML_LOG(ERROR) << "ReleaseOffscreenWindow failed err:" << ret; + OH_NativeWindow_DestroyNativeWindowBuffer(last_nativewindow_buffer_); + } + last_nativewindow_buffer_ = nullptr; + last_fence_fd_ = -1; + } + FML_LOG(INFO) << "ReleaseOffscreenWindow " << offscreen_nativewindow_; + if (offscreen_native_image_) { + // offscreen_nativewindow_ will be destroy in OH_NativeImage_Destroy. + OH_NativeImage_Destroy(&offscreen_native_image_); + offscreen_native_image_ = nullptr; + offscreen_nativewindow_ = nullptr; + } +} + +bool OHOSSurface::SetDisplayWindow(fml::RefPtr window) { + if (!window || !window->IsValid()) { + return false; + } + TRACE_EVENT0("flutter", "surface:SetDisplayWindow"); + + SkISize size = window->GetSize(); + SkISize old_size = window_size_; + window_size_ = size; + need_schedule_frame_ = false; + FML_LOG(INFO) << "SetDisplayWindow " << window->Gethandle(); + + if (native_window_ && native_window_->IsValid() && + window->Gethandle() == native_window_->Gethandle()) { + // window is same, we just set surface resize. + FML_LOG(INFO) << "window size change: (" << old_size.width() << "," + << old_size.height() << ")=>(" << size.width() << "," + << size.height() << ")"; + // Note: In vulkan mode, creating a swapchain with the same window can cause + // the process to hang (stuck on requestBuffer). Therefore, SurfaceResize is + // called here instead of directly calling SetNativeWindow. We should invoke + // this always because it may create surface again (even sizes don't + // change). In GL mode, EGLSurface will be be destroy after + // TeardownOnScreenContext. In vulkan mode, nothing will happen after + // TeardownOnScreenContext so swapchain can be reused. + OnScreenSurfaceResize(size); + return true; + } + + if (offscreen_nativewindow_ == nullptr || size.width() != offscreen_width_ || + size.height() != offscreen_height_) { + // The old swapchain must be destroyed before releasing the window to + // prevent application crashes in Vulkan. + bool ret = SetNativeWindow(window); + ReleaseOffscreenWindow(); + return ret; + } + + TRACE_EVENT0("flutter", "surface:SetNativeWindow"); + SetNativeWindow(window); + if (PaintOffscreenData(last_nativewindow_buffer_, last_fence_fd_)) { + last_nativewindow_buffer_ = nullptr; + last_fence_fd_ = -1; + } else { + need_schedule_frame_ = true; + } + + ReleaseOffscreenWindow(); + + return true; +} + +void OHOSSurface::OnFrameAvailable(void* data) { + TRACE_EVENT0("flutter", "OHOSSurface-OnFrameAvailable"); + // this callback will be invoked in raster thread because we are the producer. + + FML_LOG(INFO) << "OHOSSurface get frame data"; + std::lock_guard lock(g_surface_alive_mutex); + if (!g_surface_is_alive[(uint64_t)data]) { + return; + } + OHOSSurface* surface = (OHOSSurface*)data; + + if (surface->offscreen_native_image_ != nullptr && + surface->last_nativewindow_buffer_ != nullptr) { + // there is no consumer, so we just released it. + // OH_NativeImage_ReleaseNativeWindowBuffer will close the fence_fd even if + // it fails. + int ret = OH_NativeImage_ReleaseNativeWindowBuffer( + surface->offscreen_native_image_, surface->last_nativewindow_buffer_, + surface->last_fence_fd_); + if (ret != 0) { + // this cannot hanppen + FML_LOG(ERROR) << "release offscreen windowbuffer failed:" << ret; + OH_NativeWindow_DestroyNativeWindowBuffer( + surface->last_nativewindow_buffer_); + } + surface->last_nativewindow_buffer_ = nullptr; + surface->last_fence_fd_ = -1; + } + + int ret = OH_NativeImage_AcquireNativeWindowBuffer( + surface->offscreen_native_image_, &surface->last_nativewindow_buffer_, + &surface->last_fence_fd_); + if (surface->last_nativewindow_buffer_ == nullptr || ret != 0) { + FML_LOG(ERROR) << "acquire offscreen windowbuffer failed: " << ret; + return; + } + return; +} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/surface/ohos_surface.h b/shell/platform/ohos/surface/ohos_surface.h new file mode 100644 index 0000000000000000000000000000000000000000..747b4f617ad407608e41df37e68aca1b33118dde --- /dev/null +++ b/shell/platform/ohos/surface/ohos_surface.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_SURFACE_OHOS_SURFACE_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_SURFACE_OHOS_SURFACE_H_ + +#include +#include +#include "flutter/flow/surface.h" +#include "flutter/shell/platform/ohos/context/ohos_context.h" +#include "flutter/shell/platform/ohos/surface/ohos_native_window.h" +#include "third_party/skia/include/core/SkSize.h" + +namespace impeller { +class Context; +} // namespace impeller + +namespace flutter { + +class OHOSSurface { + public: + virtual ~OHOSSurface(); + virtual bool IsValid() const = 0; + virtual void TeardownOnScreenContext() = 0; + + virtual bool OnScreenSurfaceResize(const SkISize& size) = 0; + + virtual bool ResourceContextMakeCurrent() = 0; + + virtual bool ResourceContextClearCurrent() = 0; + + virtual bool SetNativeWindow(fml::RefPtr window) = 0; + + virtual std::unique_ptr CreateSnapshotSurface(); + + virtual std::unique_ptr CreateGPUSurface( + GrDirectContext* gr_context = nullptr) = 0; + + virtual std::shared_ptr GetImpellerContext(); + + // Return true means it will consume and release the buffer. + virtual bool PaintOffscreenData(OHNativeWindowBuffer* buffer, int fence_fd) { + return false; + }; + + // Preload GPU surface for vulkan (GPUSurface creating is time consuming in + // vulkan because of graphic pipeline building) + virtual void PrepareGpuSurface() {}; + + virtual bool PrepareOffscreenWindow(int32_t width, int32_t height); + + void ReleaseOffscreenWindow(); + + bool SetDisplayWindow(fml::RefPtr window); + + bool NeedNewFrame() { return need_schedule_frame_; } + + static void OnFrameAvailable(void* data); + + protected: + explicit OHOSSurface(const std::shared_ptr& ohos_context); + std::shared_ptr ohos_context_; + fml::RefPtr native_window_; + SkISize window_size_ = {0, 0}; + + private: + OH_NativeImage* offscreen_native_image_ = nullptr; + // + // std::vector all_offscreen_window_buffers_; + OHNativeWindow* offscreen_nativewindow_ = nullptr; + OHNativeWindowBuffer* last_nativewindow_buffer_ = nullptr; + + int32_t offscreen_width_ = 0; + int32_t offscreen_height_ = 0; + + // int32_t free_buffer_cnt_; + // int32_t max_buffer_cnt_; + int last_fence_fd_ = -1; + + bool need_schedule_frame_ = false; +}; + +class OhosSurfaceFactory { + public: + OhosSurfaceFactory() = default; + + virtual ~OhosSurfaceFactory() = default; + + virtual std::unique_ptr CreateSurface() = 0; +}; +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_OHOS_SURFACE_OHOS_SURFACE_H_ diff --git a/shell/platform/ohos/types.h b/shell/platform/ohos/types.h new file mode 100644 index 0000000000000000000000000000000000000000..e35a5cc8a00be5f2cf7425a1252c6bca91eee3f9 --- /dev/null +++ b/shell/platform/ohos/types.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_TYPES_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_TYPES_H_ +namespace flutter { + +constexpr int kPixelFmtRgba8888 = 12; + +enum Locales { + kLanguageIndex = 0, + kRegionIndex, + kScriptIndex, +}; + +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_TYPES_H_ \ No newline at end of file diff --git a/shell/platform/ohos/utils/ohos_utils.cpp b/shell/platform/ohos/utils/ohos_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bb63f5ceaeedf31b6bd10b9206f19457f4d17ae4 --- /dev/null +++ b/shell/platform/ohos/utils/ohos_utils.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 "ohos_utils.h" +namespace flutter { + +OHOSUtils::OHOSUtils(){}; +OHOSUtils::~OHOSUtils(){}; + +/** + * 将string序列化为uint8字节数组 + */ +void OHOSUtils::SerializeString(const std::string& str, + std::vector& buffer) { + // store the length of the string as a uint8_t + uint32_t length = str.size(); + buffer.insert(buffer.end(), reinterpret_cast(&length), + reinterpret_cast(&length) + sizeof(length)); + // store the actual string data + buffer.insert(buffer.end(), str.begin(), str.end()); +} + +/** + * 将map序列化为uint8字节数组 + */ +std::vector OHOSUtils::SerializeStringIntMap( + const std::map& mp) { + std::vector buffer; + + // Store the number of elements in the map + uint32_t mapSize = mp.size(); + buffer.insert(buffer.end(), reinterpret_cast(&mapSize), + reinterpret_cast(&mapSize) + sizeof(mapSize)); + + // Iterate over the map and serialize each key-value pair + for (const auto& it : mp) { + SerializeString(it.first, buffer); + buffer.insert( + buffer.end(), reinterpret_cast(&it.second), + reinterpret_cast(&it.second) + sizeof(it.second)); + } + + return buffer; +} + +/** + * convert char* type to int32_t type with a safe way + */ +void OHOSUtils::CharArrayToInt32(const char* str, int32_t& target) { + char* end; + long int num = std::strtol(str, &end, 10); + + // Check if the entire string was converted + if (*end == '\0') { + if (INT_MIN <= num && num <= INT_MAX) { + target = static_cast(num); + LOGD("The int32_t value is: %{public}d", target); + } else { + LOGE("The value of num is out of range of int32_t"); + } + } else { + target = 0; + LOGE("Conversion error, non-convertible part: %{public}s", end); + } +} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/utils/ohos_utils.h b/shell/platform/ohos/utils/ohos_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..660ec454143082ebe113bd6a7a41827e3136f04e --- /dev/null +++ b/shell/platform/ohos/utils/ohos_utils.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_UTILS_OHOS_UTILS_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_UTILS_OHOS_UTILS_H_ +#include +#include +#include +#include +#include "flutter/shell/platform/ohos/ohos_logging.h" +namespace flutter { + +class OHOSUtils { + public: + OHOSUtils(); + ~OHOSUtils(); + + static void SerializeString(const std::string& str, + std::vector& buffer); + static std::vector SerializeStringIntMap( + const std::map& mp); + static void CharArrayToInt32(const char* str, int32_t& target); +}; + +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_UTILS_OHOS_UTILS_H_ diff --git a/shell/platform/ohos/vsync_waiter_ohos.cpp b/shell/platform/ohos/vsync_waiter_ohos.cpp new file mode 100644 index 0000000000000000000000000000000000000000..213e97d18fc9728354a167cd774d34906cac3a32 --- /dev/null +++ b/shell/platform/ohos/vsync_waiter_ohos.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 "flutter/shell/platform/ohos/vsync_waiter_ohos.h" +#include +#include "fml/trace_event.h" +#include "napi_common.h" +#include "ohos_logging.h" + +namespace flutter { + +const char* flutterSyncName = "flutter_connect"; + +thread_local bool VsyncWaiterOHOS::firstCall = true; + +VsyncWaiterOHOS::VsyncWaiterOHOS(const flutter::TaskRunners& task_runners, + std::shared_ptr& enable_frame_cache) + : VsyncWaiter(task_runners), enable_frame_cache_(enable_frame_cache) { + vsyncHandle = + OH_NativeVSync_Create("flutterSyncName", strlen(flutterSyncName)); +} + +VsyncWaiterOHOS::~VsyncWaiterOHOS() { + OH_NativeVSync_Destroy(vsyncHandle); + vsyncHandle = nullptr; +} + +int64_t VsyncWaiterOHOS::GetVsyncPeriod() { + long long period = 0; + if (vsyncHandle) { + OH_NativeVSync_GetPeriod(vsyncHandle, &period); + } + return period; +} + +void VsyncWaiterOHOS::AwaitVSync() { + TRACE_EVENT0("flutter", "VsyncWaiterOHOS::AwaitVSync"); + if (vsyncHandle == nullptr) { + LOGE("AwaitVSync vsyncHandle is nullptr"); + return; + } + auto* weak_this = new std::weak_ptr(shared_from_this()); + OH_NativeVSync* handle = vsyncHandle; + + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetUITaskRunner(), [weak_this, handle]() { + int32_t ret = 0; + if (0 != (ret = OH_NativeVSync_RequestFrameWithMultiCallback( + handle, &OnVsyncFromOHOS, weak_this))) { + FML_DLOG(ERROR) << "AwaitVSync...failed:" << ret; + } + }); +} + +void VsyncWaiterOHOS::OnVsyncFromOHOS(long long timestamp, void* data) { + if (data == nullptr) { + FML_LOG(ERROR) << "VsyncWaiterOHOS::OnVsyncFromOHOS, data is nullptr."; + return; + } + if (VsyncWaiterOHOS::firstCall) { + int ret = OH_QoS_SetThreadQoS(QoS_Level::QOS_USER_INTERACTIVE); + FML_DLOG(INFO) << "qos set VsyncWaiterOHOS result:" << ret + << ",tid:" << gettid(); + VsyncWaiterOHOS::firstCall = false; + } + int64_t frame_nanos = static_cast(timestamp); + auto frame_time = fml::TimePoint::FromEpochDelta( + fml::TimeDelta::FromNanoseconds(frame_nanos)); + + auto* weak_this = reinterpret_cast*>(data); + uint64_t vsync_period = 0; + auto shared_this = weak_this->lock(); + if (shared_this) { + auto ohos_vsync_waiter = static_cast(shared_this.get()); + vsync_period = ohos_vsync_waiter->GetVsyncPeriod(); + // To avoid excessive response latency, frames will not be cached when the + // refresh rate is 60 Hz. + if (*ohos_vsync_waiter->enable_frame_cache_ && vsync_period < 15000000) { + // When the frame cache is enabled, one frame will be cached, sacrificing + // one frame of latency in exchange for smoothness. + vsync_period += vsync_period - 1000000; + } + } + + // [-1ms] is to avoid this situation: + // ui_timestamp(xxx8.334ms) > now_time(xxx8.332ms) => skip this frame + // update [-2ms]: vsync may get a perid of 7.1 ms when 120hz. + auto target_time = frame_time + + fml::TimeDelta::FromNanoseconds(vsync_period) - + fml::TimeDelta::FromMilliseconds(2); + std::string trace_str = + std::to_string(timestamp) + "-" + std::to_string(vsync_period) + "-" + + std::to_string(target_time.ToEpochDelta().ToNanoseconds()); + TRACE_EVENT1("flutter", "OHOSVsync", "timestamp-period", trace_str.c_str()); + ConsumePendingCallback(weak_this, frame_time, target_time); +} + +void VsyncWaiterOHOS::ConsumePendingCallback( + std::weak_ptr* weak_this, + fml::TimePoint frame_start_time, + fml::TimePoint frame_target_time) { + std::shared_ptr shared_this = weak_this->lock(); + delete weak_this; + + if (shared_this) { + shared_this->FireCallback(frame_start_time, frame_target_time); + } +} + +} // namespace flutter diff --git a/shell/platform/ohos/vsync_waiter_ohos.h b/shell/platform/ohos/vsync_waiter_ohos.h new file mode 100644 index 0000000000000000000000000000000000000000..71986965a96dc9fdd27b4aa51bc8b1ff73d17b06 --- /dev/null +++ b/shell/platform/ohos/vsync_waiter_ohos.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * 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 FLUTTER_SHELL_PLATFORM_OHOS_VSYNC_WAITER_OHOS_H_ +#define FLUTTER_SHELL_PLATFORM_OHOS_VSYNC_WAITER_OHOS_H_ +#include + +#include +#include "flutter/fml/macros.h" +#include "flutter/shell/common/vsync_waiter.h" + +namespace flutter { + +class VsyncWaiterOHOS final : public VsyncWaiter { + public: + explicit VsyncWaiterOHOS(const flutter::TaskRunners& task_runners, + std::shared_ptr& enable_frame_cache); + + int64_t GetVsyncPeriod(); + + ~VsyncWaiterOHOS() override; + + private: + thread_local static bool firstCall; + // |VsyncWaiter| + void AwaitVSync() override; + + static void OnVsyncFromOHOS(long long timestamp, void* data); + static void ConsumePendingCallback(std::weak_ptr* weak_this, + fml::TimePoint frame_start_time, + fml::TimePoint frame_target_time); + + OH_NativeVSync* vsyncHandle; + std::shared_ptr enable_frame_cache_; + FML_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterOHOS); +}; +} // namespace flutter +#endif // FLUTTER_SHELL_PLATFORM_OHOS_VSYNC_WAITER_OHOS_H_ \ No newline at end of file diff --git a/shell/version/version.gni b/shell/version/version.gni index 859f6c2fd61f21048e192b0512470feed7391214..39fa27d3636bc93511d1a5daf958e5748f25e244 100644 --- a/shell/version/version.gni +++ b/shell/version/version.gni @@ -5,11 +5,9 @@ import("//flutter/build/dart/dart.gni") declare_args() { - engine_version = "" - - skia_version = "" - - dart_version = "" + engine_version = "1a65d409c7a1438a34d21b60bf30a6fd5db59314" + skia_version = "7e1844439eaa3eb24d00c6314ddc81ca532fdd1b" + dart_version = "5b8936496673a285bf8f37472119bc2afb38a295" } _flutter_root = "//flutter" diff --git a/skia/BUILD.gn b/skia/BUILD.gn index ef594071b5ef107a31ca52990d0343baf5e0a7e9..ad99fce2db9287b4bded519ad3779983e635b6f8 100644 --- a/skia/BUILD.gn +++ b/skia/BUILD.gn @@ -226,6 +226,40 @@ optional("fontmgr_android") { ] } +optional("fontmgr_ohos") { + enabled = skia_enable_fontmgr_ohos + deps = [ + ":typeface_freetype", + "//flutter/third_party/expat", + "//third_party/jsoncpp:jsoncpp", + ] + public = [ + "$_skia_root/src/ports/skia_ohos/FontConfig_ohos.h", + "$_skia_root/src/ports/skia_ohos/FontInfo_ohos.h", + "$_skia_root/src/ports/skia_ohos/SkFontMgr_ohos.h", + "$_skia_root/src/ports/skia_ohos/SkFontStyleSet_ohos.h", + "$_skia_root/src/ports/skia_ohos/SkTypeface_ohos.h", + ] + sources = [ + "$_skia_root/src/ports/skia_ohos/FontConfig_ohos.cpp", + "$_skia_root/src/ports/skia_ohos/SkFontMgr_ohos.cpp", + "$_skia_root/src/ports/skia_ohos/SkFontStyleSet_ohos.cpp", + "$_skia_root/src/ports/skia_ohos/SkTypeface_ohos.cpp", + "//flutter/third_party/vulkan-deps/spirv-headers/src/tools/buildHeaders/jsoncpp/dist/jsoncpp.cpp", + ] + include_dirs = [ + "$_skia_root/include/private", + "$_skia_root/include/private/base", + "$_skia_root/include/ports", + "$_skia_root/include/core", + "$_skia_root/src/core", + "$_skia_root/src/ports", + "$_skia_root/src/ports/skia_ohos", + "$_skia_root/src/core/SkFontDescriptor.h", + "//flutter/third_party/vulkan-deps/spirv-headers/src/tools/buildHeaders/jsoncpp/dist/", + ] +} + optional("fontmgr_custom") { enabled = skia_enable_fontmgr_custom_directory || @@ -429,6 +463,11 @@ optional("gpu") { if (defined(ndk_api) && ndk_api >= 26) { libs += [ "android" ] } + } else if (is_ohos) { + sources += [ + "$_skia_root/src/gpu/ganesh/gl/egl/GrGLMakeEGLInterface.cpp", + "$_skia_root/src/gpu/ganesh/gl/egl/GrGLMakeNativeInterface_egl.cpp", + ] } else if (skia_use_webgl) { sources += [ "$_skia_root/src/gpu/ganesh/gl/webgl/GrGLMakeNativeInterface_webgl.cpp", @@ -620,6 +659,7 @@ skia_component("skia") { ":fontmgr_fontconfig", ":fontmgr_fuchsia", ":fontmgr_mac_ct", + ":fontmgr_ohos", ":fontmgr_win", ":gpu", ":jpeg_encode", @@ -722,6 +762,19 @@ skia_component("skia") { ] } + if (is_ohos) { + sources += [ "$_skia_root/src/ports/SkDebug_ohos.cpp" ] + libs += [ + "EGL", + "GLESv3", + "hilog_ndk.z", + ] + defines += [ + "TARGET_OS_OHOS", + "SK_BUILD_FOR_OHOS", + ] + } + if (is_linux || is_wasm) { sources += [ "$_skia_root/src/ports/SkDebug_stdio.cpp" ] if (skia_use_egl) { diff --git a/testing/resources/canvas_test_dithered_gradient.png b/testing/resources/canvas_test_dithered_gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..d8062f2dc1f35424af652e5580097b4c8b1a1d50 Binary files /dev/null and b/testing/resources/canvas_test_dithered_gradient.png differ diff --git a/testing/resources/canvas_test_gradient.png b/testing/resources/canvas_test_gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..89f3f63dc518a1dfd84c9052125fdf6ac079253f Binary files /dev/null and b/testing/resources/canvas_test_gradient.png differ diff --git a/testing/resources/canvas_test_toImage.png b/testing/resources/canvas_test_toImage.png new file mode 100644 index 0000000000000000000000000000000000000000..75d02c2fa967c12473c6a2204ab66aac59023132 Binary files /dev/null and b/testing/resources/canvas_test_toImage.png differ diff --git a/testing/resources/dotted_path_effect_mixed_with_stroked_geometry.png b/testing/resources/dotted_path_effect_mixed_with_stroked_geometry.png new file mode 100644 index 0000000000000000000000000000000000000000..40eb84ed251aaf81f8452450abac73265cd5ad4c Binary files /dev/null and b/testing/resources/dotted_path_effect_mixed_with_stroked_geometry.png differ diff --git a/testing/resources/performance_overlay_gold_120fps.png b/testing/resources/performance_overlay_gold_120fps.png index 9677724230ad25a4580c7c0309d6af49b2952ad1..c19d1eb7208e1ee768b0031404592fa26e65ffd5 100644 Binary files a/testing/resources/performance_overlay_gold_120fps.png and b/testing/resources/performance_overlay_gold_120fps.png differ diff --git a/testing/resources/performance_overlay_gold_60fps.png b/testing/resources/performance_overlay_gold_60fps.png index 0d45210c56e406a7c66e173d93f6f215c91ccaa3..4e18fa15e94365c57a3a06bd934ccc56782deb40 100644 Binary files a/testing/resources/performance_overlay_gold_60fps.png and b/testing/resources/performance_overlay_gold_60fps.png differ diff --git a/testing/resources/performance_overlay_gold_90fps.png b/testing/resources/performance_overlay_gold_90fps.png index d6fb7e8e0d7a1eeeda8ae757408a376a1e251736..962e2875d4fde53aa45a76081099ee76bc5a7330 100644 Binary files a/testing/resources/performance_overlay_gold_90fps.png and b/testing/resources/performance_overlay_gold_90fps.png differ diff --git a/testing/resources/text_with_gradient_with_matrix.png b/testing/resources/text_with_gradient_with_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..b940c1ee4e08b4f25c6ab2dd02210a638808e4a1 Binary files /dev/null and b/testing/resources/text_with_gradient_with_matrix.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_bogus_font_text_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_bogus_font_text_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..a5948725a2c09d3337dd1fe6b6dd1ada06abf8c4 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_bogus_font_text_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_non_full_screen_flutter_view_platform_view_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_non_full_screen_flutter_view_platform_view_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..9f809270be5d7bd8606cc6ee99c660b904858b5a Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_non_full_screen_flutter_view_platform_view_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..9f3e63bd8942402a97cf5dea84e1aac631362982 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_with_transform_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_with_transform_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..83fb5a6231e78883a723cc9689066c1215fb6540 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_with_transform_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprect_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprect_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..06893474b07ae213e8e2892c01b57591025c1ba2 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprect_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprect_with_transform_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprect_with_transform_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..c44327511d800967e51edd9bf73cc243db349d5e Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprect_with_transform_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprrect_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprrect_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..7de337102cc5ddff26c6a3e78e64c297e7c8b28e Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprrect_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprrect_with_transform_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprrect_with_transform_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..c47192713cf4e861db8ddda66de7ed252ea775c5 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprrect_with_transform_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..b91cf5569e582f13f1c5840d18987c8bf91e7c50 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..a6965956007fa4182f9f643e753e31f906dd0332 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..f64d163c510ed490228a3127a0b0cb9c4fe8d3c8 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_multiple_background_foreground_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_multiple_background_foreground_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..22dd4aa5f18e2d9ad5d9bdc0e6b16c68f6ce30ca Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_multiple_background_foreground_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_multiple_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_multiple_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..cc26a0baa7785d38e2fc204427afe43b0b93de29 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_multiple_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_opacity_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_opacity_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..7057d52dfa0a9538570dd62b7fa72944608b5531 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_opacity_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_rotate_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_rotate_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..3548e72f7335a6e39f57a338c28200fc1de43ac4 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_rotate_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..570aa9a7d3f51c07978390e579079e542af13823 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_with_other_backdrop_filter_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_with_other_backdrop_filter_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..0e30ba37d13980fe526d8967baecc87d34402662 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_with_other_backdrop_filter_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_spawn_engine_works_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_spawn_engine_works_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..a5948725a2c09d3337dd1fe6b6dd1ada06abf8c4 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_spawn_engine_works_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_two_platform_views_with_other_backdrop_filter_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_two_platform_views_with_other_backdrop_filter_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..9996a2e5c155c7f68ab25176d2e20a2dbb4154f2 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_two_platform_views_with_other_backdrop_filter_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 5d1099a522350c983962ee4f1bb8538155a3df03..dabf0893e2cede435d9a2d7367f9fe8c78112c4a 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -802,6 +802,7 @@ class PlatformViewLargeClipRRectScenario extends PlatformViewScenario { } } + /// Platform view with clip path. class PlatformViewClipPathScenario extends PlatformViewScenario { /// Constructs a platform view with clip path scenario. @@ -962,6 +963,7 @@ class PlatformViewLargeClipRRectWithTransformScenario extends PlatformViewScenar } } + /// Platform view with clip path after transformed. class PlatformViewClipPathWithTransformScenario extends PlatformViewScenario { /// Constructs a platform view with clip path with transform scenario. diff --git a/third_party/txt/BUILD.gn b/third_party/txt/BUILD.gn index 9079ed158530064bcd69452ae074acb18fde4afa..33996affb8a5765022a174a310af09da1eb0c5e7 100644 --- a/third_party/txt/BUILD.gn +++ b/third_party/txt/BUILD.gn @@ -108,6 +108,8 @@ source_set("txt") { deps += [ "//flutter/fml" ] } else if (is_android) { sources += [ "src/txt/platform_android.cc" ] + } else if (is_ohos) { + sources += [ "src/txt/platform_ohos.cc" ] } else if (is_linux) { sources += [ "src/txt/platform_linux.cc" ] } else if (is_fuchsia) { diff --git a/third_party/txt/src/txt/platform_ohos.cc b/third_party/txt/src/txt/platform_ohos.cc new file mode 100644 index 0000000000000000000000000000000000000000..ba548a105dfa3cca4069f7fadf8f9b13c2f23768 --- /dev/null +++ b/third_party/txt/src/txt/platform_ohos.cc @@ -0,0 +1,31 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* 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 "txt/platform.h" +#include "third_party/skia/include/ports/SkFontMgr_ohos_api.h" + +namespace txt { + +std::vector GetDefaultFontFamilies() { + return {"sans-serif"}; +} + +sk_sp GetDefaultFontManager(uint32_t font_initialization_data) { + static sk_sp mgr = SkFontMgr_New_OHOS(); + return mgr; +} + +} // namespace txt + diff --git a/third_party/txt/third_party/fonts/Bold.ttf b/third_party/txt/third_party/fonts/Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..44ef33cf89e6ea8e913879981a1ce110d0122d22 Binary files /dev/null and b/third_party/txt/third_party/fonts/Bold.ttf differ diff --git a/third_party/txt/third_party/fonts/BoldItalic.ttf b/third_party/txt/third_party/fonts/BoldItalic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..10c7b02b955c0f67c51f83820715a3a1a3274798 Binary files /dev/null and b/third_party/txt/third_party/fonts/BoldItalic.ttf differ diff --git a/third_party/txt/third_party/fonts/ColorEmojiFont.ttf b/third_party/txt/third_party/fonts/ColorEmojiFont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..eee1c379f0314d1b2ce632a71bc62b6b19d6d82f Binary files /dev/null and b/third_party/txt/third_party/fonts/ColorEmojiFont.ttf differ diff --git a/third_party/txt/third_party/fonts/ColorTextMixedEmojiFont.ttf b/third_party/txt/third_party/fonts/ColorTextMixedEmojiFont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..57ee330b79f66be79c74b7f18e323a178620df58 Binary files /dev/null and b/third_party/txt/third_party/fonts/ColorTextMixedEmojiFont.ttf differ diff --git a/third_party/txt/third_party/fonts/DroidSerif.ttf b/third_party/txt/third_party/fonts/DroidSerif.ttf new file mode 100644 index 0000000000000000000000000000000000000000..90cdfffe540c8440456c354061ad2aabc9c0985f Binary files /dev/null and b/third_party/txt/third_party/fonts/DroidSerif.ttf differ diff --git a/third_party/txt/third_party/fonts/Emoji.ttf b/third_party/txt/third_party/fonts/Emoji.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a3413b3e08f96ab5b2a0b5cffd1cc249026ffd40 Binary files /dev/null and b/third_party/txt/third_party/fonts/Emoji.ttf differ diff --git a/third_party/txt/third_party/fonts/Italic.ttf b/third_party/txt/third_party/fonts/Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9593229f925d53632c3a1d8b28e538ed96d90937 Binary files /dev/null and b/third_party/txt/third_party/fonts/Italic.ttf differ diff --git a/third_party/txt/third_party/fonts/Ja.ttf b/third_party/txt/third_party/fonts/Ja.ttf new file mode 100644 index 0000000000000000000000000000000000000000..b3d4e848ecf02d71b1cdf7ddc2cc4e649a7ae1cd Binary files /dev/null and b/third_party/txt/third_party/fonts/Ja.ttf differ diff --git a/third_party/txt/third_party/fonts/Katibeh-Regular.ttf b/third_party/txt/third_party/fonts/Katibeh-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ccbb8f0de2ad5c5afaec2fc8f5b642b804ab6928 Binary files /dev/null and b/third_party/txt/third_party/fonts/Katibeh-Regular.ttf differ diff --git a/third_party/txt/third_party/fonts/Ko.ttf b/third_party/txt/third_party/fonts/Ko.ttf new file mode 100644 index 0000000000000000000000000000000000000000..51f6c4ede8b4d02b18bfd591d2a478e13b5342b0 Binary files /dev/null and b/third_party/txt/third_party/fonts/Ko.ttf differ diff --git a/third_party/txt/third_party/fonts/MultiAxis.ttf b/third_party/txt/third_party/fonts/MultiAxis.ttf new file mode 100644 index 0000000000000000000000000000000000000000..1d687cb367d740ad61bf6e3c633ac76732d05c91 Binary files /dev/null and b/third_party/txt/third_party/fonts/MultiAxis.ttf differ diff --git a/third_party/txt/third_party/fonts/NoCmapFormat14.ttf b/third_party/txt/third_party/fonts/NoCmapFormat14.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2a0c46c7aca19c80833cf8f1a3f0e1e39be7b232 Binary files /dev/null and b/third_party/txt/third_party/fonts/NoCmapFormat14.ttf differ diff --git a/third_party/txt/third_party/fonts/NoGlyphFont.ttf b/third_party/txt/third_party/fonts/NoGlyphFont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0243f820408af7cd33497de3b01c19e43ab71e67 Binary files /dev/null and b/third_party/txt/third_party/fonts/NoGlyphFont.ttf differ diff --git a/third_party/txt/third_party/fonts/NotoSansCJK-Regular.ttc b/third_party/txt/third_party/fonts/NotoSansCJK-Regular.ttc new file mode 100755 index 0000000000000000000000000000000000000000..2dd4a60625dbf417cc25a6948250546501c01a59 Binary files /dev/null and b/third_party/txt/third_party/fonts/NotoSansCJK-Regular.ttc differ diff --git a/third_party/txt/third_party/fonts/NotoSansKhmer-Regular.ttf b/third_party/txt/third_party/fonts/NotoSansKhmer-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9aaea35610f1fb4d139afb004d3c757d3a831f3e Binary files /dev/null and b/third_party/txt/third_party/fonts/NotoSansKhmer-Regular.ttf differ diff --git a/third_party/txt/third_party/fonts/Regular.ttf b/third_party/txt/third_party/fonts/Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ab638f58de03bb8514e197653aee37795765ad39 Binary files /dev/null and b/third_party/txt/third_party/fonts/Regular.ttf differ diff --git a/third_party/txt/third_party/fonts/Roboto-Black.ttf b/third_party/txt/third_party/fonts/Roboto-Black.ttf new file mode 100644 index 0000000000000000000000000000000000000000..689fe5cb3c715f2944fec30e43ccb8a2b10625d3 Binary files /dev/null and b/third_party/txt/third_party/fonts/Roboto-Black.ttf differ diff --git a/third_party/txt/third_party/fonts/Roboto-BlackItalic.ttf b/third_party/txt/third_party/fonts/Roboto-BlackItalic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0b4e0ee108899ddfef739f48d2aa9475b8b41a03 Binary files /dev/null and b/third_party/txt/third_party/fonts/Roboto-BlackItalic.ttf differ diff --git a/third_party/txt/third_party/fonts/Roboto-Bold.ttf b/third_party/txt/third_party/fonts/Roboto-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d3f01ad245b628f386ac95786f53167038720eb2 Binary files /dev/null and b/third_party/txt/third_party/fonts/Roboto-Bold.ttf differ diff --git a/third_party/txt/third_party/fonts/Roboto-BoldItalic.ttf b/third_party/txt/third_party/fonts/Roboto-BoldItalic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..41cc1e753153e343a0ab73a341f545fec9eb7816 Binary files /dev/null and b/third_party/txt/third_party/fonts/Roboto-BoldItalic.ttf differ diff --git a/third_party/txt/third_party/fonts/Roboto-Italic.ttf b/third_party/txt/third_party/fonts/Roboto-Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6a1cee5b2948dbddf8fe6bb050a5cdca1c206dbf Binary files /dev/null and b/third_party/txt/third_party/fonts/Roboto-Italic.ttf differ diff --git a/third_party/txt/third_party/fonts/Roboto-Light.ttf b/third_party/txt/third_party/fonts/Roboto-Light.ttf new file mode 100644 index 0000000000000000000000000000000000000000..219063a578a486b7c00262057efcbc44ebab0eeb Binary files /dev/null and b/third_party/txt/third_party/fonts/Roboto-Light.ttf differ diff --git a/third_party/txt/third_party/fonts/Roboto-LightItalic.ttf b/third_party/txt/third_party/fonts/Roboto-LightItalic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0e81e876fcab86b7cc99356aa0e19c0916a907bf Binary files /dev/null and b/third_party/txt/third_party/fonts/Roboto-LightItalic.ttf differ diff --git a/third_party/txt/third_party/fonts/Roboto-MediumItalic.ttf b/third_party/txt/third_party/fonts/Roboto-MediumItalic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..003029527cc651faa12b18f02ce81d74f5031756 Binary files /dev/null and b/third_party/txt/third_party/fonts/Roboto-MediumItalic.ttf differ diff --git a/third_party/txt/third_party/fonts/Roboto-Thin.ttf b/third_party/txt/third_party/fonts/Roboto-Thin.ttf new file mode 100644 index 0000000000000000000000000000000000000000..b74a4fd1a2ed1960da1d0f8f0e9b8d05a5819000 Binary files /dev/null and b/third_party/txt/third_party/fonts/Roboto-Thin.ttf differ diff --git a/third_party/txt/third_party/fonts/Roboto-ThinItalic.ttf b/third_party/txt/third_party/fonts/Roboto-ThinItalic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..dd0ddb852645db815ce4f3d45ec4a37b601ccb5f Binary files /dev/null and b/third_party/txt/third_party/fonts/Roboto-ThinItalic.ttf differ diff --git a/third_party/txt/third_party/fonts/SourceHanSerifCN-Bold.otf b/third_party/txt/third_party/fonts/SourceHanSerifCN-Bold.otf new file mode 100644 index 0000000000000000000000000000000000000000..77656b1999c6221628ee78cb5ff5906fe02278a4 Binary files /dev/null and b/third_party/txt/third_party/fonts/SourceHanSerifCN-Bold.otf differ diff --git a/third_party/txt/third_party/fonts/SourceHanSerifCN-Regular.otf b/third_party/txt/third_party/fonts/SourceHanSerifCN-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..c5930e7d2774c93ad01bb63cd74f8dcc37e61c45 Binary files /dev/null and b/third_party/txt/third_party/fonts/SourceHanSerifCN-Regular.otf differ diff --git a/third_party/txt/third_party/fonts/TextEmojiFont.ttf b/third_party/txt/third_party/fonts/TextEmojiFont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6a5e9d94ec1656eb572a2168b8dfc8d26d8a7dcf Binary files /dev/null and b/third_party/txt/third_party/fonts/TextEmojiFont.ttf differ diff --git a/third_party/txt/third_party/fonts/UnicodeBMPOnly.ttf b/third_party/txt/third_party/fonts/UnicodeBMPOnly.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8196669fa18c582e1f9c1520015c8203055912df Binary files /dev/null and b/third_party/txt/third_party/fonts/UnicodeBMPOnly.ttf differ diff --git a/third_party/txt/third_party/fonts/UnicodeBMPOnly2.ttf b/third_party/txt/third_party/fonts/UnicodeBMPOnly2.ttf new file mode 100644 index 0000000000000000000000000000000000000000..c14b1950054993938d0c6269a651d02a78eaf207 Binary files /dev/null and b/third_party/txt/third_party/fonts/UnicodeBMPOnly2.ttf differ diff --git a/third_party/txt/third_party/fonts/UnicodeUCS4.ttf b/third_party/txt/third_party/fonts/UnicodeUCS4.ttf new file mode 100644 index 0000000000000000000000000000000000000000..354e1a370f4bf1c5e7e31ed270729a17731676be Binary files /dev/null and b/third_party/txt/third_party/fonts/UnicodeUCS4.ttf differ diff --git a/third_party/txt/third_party/fonts/VariationSelectorTest-Regular.ttf b/third_party/txt/third_party/fonts/VariationSelectorTest-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0504c67fb7a6403167ac8b53ae177c7b53d7912c Binary files /dev/null and b/third_party/txt/third_party/fonts/VariationSelectorTest-Regular.ttf differ diff --git a/third_party/txt/third_party/fonts/ZhHans.ttf b/third_party/txt/third_party/fonts/ZhHans.ttf new file mode 100644 index 0000000000000000000000000000000000000000..08adc1b300f8013b64519eeeaf1645afaead87b9 Binary files /dev/null and b/third_party/txt/third_party/fonts/ZhHans.ttf differ diff --git a/third_party/txt/third_party/fonts/ZhHant.ttf b/third_party/txt/third_party/fonts/ZhHant.ttf new file mode 100644 index 0000000000000000000000000000000000000000..592117d5a782f98cfd9eff5ef390eb4907d07d1f Binary files /dev/null and b/third_party/txt/third_party/fonts/ZhHant.ttf differ diff --git a/tools/gn b/tools/gn index 0be76e5477334c76c6590bb348666b14e6af60a0..4892328d3cc01f5a665864a8244392a97be29d77 100755 --- a/tools/gn +++ b/tools/gn @@ -53,6 +53,10 @@ def get_out_dir(args): if args.linux_cpu is not None: target_dir.append(args.linux_cpu) + +# if args.ohos_cpu is not None: +# target_dir.append(args.ohos_cpu) + if args.windows_cpu != 'x64': target_dir.append(args.windows_cpu) @@ -99,8 +103,10 @@ def is_host_build(args): # target_os='linux' and linux-cpu='arm64' if args.target_os == 'linux' and args.linux_cpu == 'arm64': return True - # The Mac and host targets are redundant. Again, necessary to disambiguate - # during cross-compilation. +#if args.target_os == 'ohos' and args.ohos_cpu == 'arm64': +# return True +# The Mac and host targets are redundant. Again, necessary to disambiguate +# during cross-compilation. if args.target_os == 'mac': return True return False @@ -121,6 +127,9 @@ def can_use_prebuilt_dart(args): prebuilt = 'linux-x64' elif args.target_os == 'linux' and args.linux_cpu in ['x64', 'arm64']: prebuilt = 'linux-%s' % args.linux_cpu + elif args.target_os == 'ohos' and args.ohos_cpu in ['x64', 'arm64']: + prebuilt = 'linux-%s' % args.ohos_cpu + #prebuilt = 'ohos-%s' % args.ohos_cpu elif args.target_os == 'mac' and args.mac_cpu in ['x64', 'arm64']: prebuilt = 'macos-%s' % args.mac_cpu elif args.target_os == 'win' and args.windows_cpu in ['x64', 'arm64']: @@ -146,6 +155,7 @@ def get_host_os(): def is_rosetta(): if platform.system() == 'Darwin': proc = subprocess.Popen(['sysctl', '-in', 'sysctl.proc_translated'], + shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output, _ = proc.communicate() @@ -185,6 +195,8 @@ def get_target_cpu(args): return args.mac_cpu if args.target_os == 'linux': return args.linux_cpu + if args.target_os == 'ohos': + return args.ohos_cpu if args.target_os == 'fuchsia': return args.fuchsia_cpu if args.target_os == 'wasm': @@ -455,6 +467,7 @@ def to_gn_args(args): if args.target_os != 'ios': raise Exception('--simulator is only supported for iOS') + print('args.target_os:%s' % args.target_os) runtime_mode = args.runtime_mode gn_args = {} @@ -502,6 +515,10 @@ def to_gn_args(args): gn_args['skia_use_icu'] = True gn_args['is_official_build'] = True # Disable Skia test utilities. gn_args['android_full_debug'] = args.target_os == 'android' and args.unoptimized + + if args.target_os == 'ohos' and get_host_os() != 'win': + gn_args['ohos_api_int'] = args.ohos_api_int + if args.clang is None: gn_args['is_clang'] = True else: @@ -532,6 +549,7 @@ def to_gn_args(args): # Set OS, CPU arch for host or target build. if is_host_build(args): + print('is host build ') gn_args['host_os'] = get_host_os() gn_args['host_cpu'] = get_host_cpu() gn_args['target_os'] = gn_args['host_os'] @@ -541,7 +559,10 @@ def to_gn_args(args): gn_args['target_os'] = args.target_os gn_args['target_cpu'] = get_target_cpu(args) gn_args['dart_target_arch'] = gn_args['target_cpu'] + if (args.target_os == 'ohos'): + gn_args['host_cpu'] = get_host_cpu() + print("ARGS.target_os:%s-%s" % (args.target_os, gn_args['target_os'])) if not args.build_engine_artifacts: gn_args['flutter_build_engine_artifacts'] = False @@ -667,7 +688,7 @@ def to_gn_args(args): else: gn_args['skia_use_gl'] = args.target_os != 'fuchsia' - if sys.platform == 'darwin' and args.target_os not in ['android', 'fuchsia']: + if sys.platform == 'darwin' and args.target_os not in ['android', 'fuchsia', 'ohos']: # OpenGL is deprecated on macOS > 10.11. # This is not necessarily needed but enabling this until we have a way to # build a macOS metal only shell and a gl only shell. @@ -678,7 +699,7 @@ def to_gn_args(args): # Enable Vulkan on all platforms except for iOS. This is just # to save on mobile binary size, as there's no reason the Vulkan embedder # features can't work on these platforms. - if gn_args['target_os'] not in ['ios', 'mac']: + if gn_args['target_os'] not in ['ios', 'mac', 'ohos']: gn_args['skia_use_vulkan'] = True gn_args['skia_use_vma'] = False gn_args['shell_enable_vulkan'] = True @@ -692,7 +713,9 @@ def to_gn_args(args): # gn_args['enable_profiling'] = args.runtime_mode != 'release' and args.android_cpu != 'x86' # Make symbols visible in order to enable symbolization of unit test crash backtraces on Linux - gn_args['disable_hidden_visibility'] = args.target_os == 'linux' and args.unoptimized + gn_args['disable_hidden_visibility'] = ( + args.target_os == 'linux' or args.target_os == 'ohos' + ) and args.unoptimized if args.arm_float_abi: gn_args['arm_float_abi'] = args.arm_float_abi @@ -1003,6 +1026,7 @@ def parse_args(args): ) parser.add_argument('--simulator', action='store_true', default=False) parser.add_argument('--linux', dest='target_os', action='store_const', const='linux') + parser.add_argument('--ohos', dest='target_os', action='store_const', const='ohos') parser.add_argument('--fuchsia', dest='target_os', action='store_const', const='fuchsia') parser.add_argument('--wasm', dest='target_os', action='store_const', const='wasm') parser.add_argument( @@ -1016,6 +1040,9 @@ def parse_args(args): parser.add_argument('--windows', dest='target_os', action='store_const', const='win') parser.add_argument('--linux-cpu', type=str, choices=['x64', 'x86', 'arm64', 'arm']) + parser.add_argument('--ohos-cpu', type=str, choices=['x64', 'x86', 'arm64', 'arm']) + parser.add_argument('--ohos-api-int', type=int, default=13) + parser.add_argument('--fuchsia-cpu', type=str, choices=['x64', 'arm64'], default='x64') parser.add_argument('--windows-cpu', type=str, choices=['x64', 'arm64', 'x86'], default='x64') parser.add_argument('--simulator-cpu', type=str, choices=['x64', 'arm64'], default='x64') @@ -1346,6 +1373,16 @@ def main(argv): '--export-compile-commands', ] + print('XX ohos_cpu:%s' % args.ohos_cpu) + # print('linux_cpu:'+ args.linux_cpu ) + print('XX target_os:%s' % args.target_os) + + if args.target_os == 'ohos': + if args.ohos_cpu is not None: + args.linux_cpu = args.ohos_cpu + elif args.linux_cpu is not None: + args.ohos_cpu = args.linux_cpu + if not args.web: if args.ide != '': command.append('--ide=%s' % args.ide) @@ -1367,6 +1404,8 @@ def main(argv): command.append(out_dir) command.append('--args=%s' % ' '.join(gn_args)) + print('gn ARGS : %s' % ' '.join(gn_args)) + if args.trace_gn: command.append('--tracelog=%s/gn_trace.json' % out_dir) diff --git a/tools/path_ops/dart/lib/path_ops.dart b/tools/path_ops/dart/lib/path_ops.dart index a749738c382d3151b5cd21938acda208ce49e74d..fad2edd59433a5ef20fa092fb62e3ec8c80418b4 100644 --- a/tools/path_ops/dart/lib/path_ops.dart +++ b/tools/path_ops/dart/lib/path_ops.dart @@ -297,7 +297,7 @@ final ffi.DynamicLibrary _dylib = () { return ffi.DynamicLibrary.open('path_ops.dll'); } else if (Platform.isIOS || Platform.isMacOS) { return ffi.DynamicLibrary.open('libpath_ops.dylib'); - } else if (Platform.isAndroid || Platform.isLinux) { + } else if (Platform.isAndroid || Platform.isLinux || Platform.isOhos) { return ffi.DynamicLibrary.open('libpath_ops.so'); } throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}'); diff --git a/vulkan/BUILD.gn b/vulkan/BUILD.gn index 8dce31a7684dce24587788ee00c6bc92d6e8c3c6..cf2518a0ecf41e2d00d7b398cf97fdc33770dd27 100644 --- a/vulkan/BUILD.gn +++ b/vulkan/BUILD.gn @@ -52,6 +52,9 @@ source_set("vulkan") { public_configs = [ "//flutter:config" ] - public_deps = - [ "//flutter/third_party/vulkan-deps/vulkan-headers/src:vulkan_headers" ] + if (!is_ohos) { + public_deps = [ + "//flutter/third_party/vulkan-deps/vulkan-headers/src:vulkan_headers", + ] + } }