Merge branch 'main' into srawlins-patch-1
diff --git a/DEPS b/DEPS
index 325b82b..4fdea30 100644
--- a/DEPS
+++ b/DEPS
@@ -14,7 +14,7 @@
'flutter_git': 'https://flutter.googlesource.com',
'skia_git': 'https://skia.googlesource.com',
'llvm_git': 'https://llvm.googlesource.com',
- 'skia_revision': '539ab9d398121cb2dc9a6d20a0643a59a4c15e02',
+ 'skia_revision': '3a081993e2a740118839621e0b0fd206e045987e',
# WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY
# See `lib/web_ui/README.md` for how to roll CanvasKit to a new version.
@@ -56,28 +56,28 @@
# Dart is: https://github.com/dart-lang/sdk/blob/main/DEPS
# You can use //tools/dart/create_updated_flutter_deps.py to produce
# updated revision list of existing dependencies.
- 'dart_revision': 'd916a5f69a486de98316900f19ef0ff46834b03d',
+ 'dart_revision': '993d3069f42e98b2b29e441bc98424065cc255ca',
# WARNING: DO NOT EDIT MANUALLY
# The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py
- 'dart_binaryen_rev': '459bc0797f67cb2a8fd4598bb7143b34036608d9',
+ 'dart_binaryen_rev': '93883fde36ac158fd415dcd6dbd387dcfd928d3c',
'dart_boringssl_gen_rev': 'fef055e8d2749b82c79c8f043be1cbe5e8e4b40c',
'dart_boringssl_rev': '2db0eb3f96a5756298dcd7f9319e56a98585bd10',
'dart_browser_launcher_rev': 'e5fc5d488eb5038bfec2a6690c72ab8dd353e101',
'dart_clock_rev': '7956d60042f4ea979c4554d43eeb57d087627869',
- 'dart_collection_rev': '24b75d85df6a26aac7be13b56ff1ce4360c04a64',
+ 'dart_collection_rev': '887b826b50f48d6a9cd2c0684aa353e8e3a0fad0',
'dart_devtools_rev': 'dcef4f6efe986f110f55dbbe7f3731802e86690a',
'dart_libprotobuf_rev': '24487dd1045c7f3d64a21f38a3f0c06cc4cf2edb',
'dart_perfetto_rev': '13ce0c9e13b0940d2476cd0cff2301708a9a2e2b',
'dart_protobuf_gn_rev': 'ca669f79945418f6229e4fef89b666b2a88cbb10',
'dart_protobuf_rev': 'ccf104dbc36929c0f8708285d5f3a8fae206343e',
'dart_pub_rev': '1efd3f5e274e153637d99698b0ee454f6f2550ab',
- 'dart_tools_rev': 'd4995d47b99d5e9564abfed2218f4a23df75983b',
+ 'dart_tools_rev': 'f882de9ba86712003728d4663e1b73a620d352b1',
'dart_watcher_rev': '3b850778ad0b62db3aa2cfe48832870c2461db30',
'dart_web_rev': '8478cd27d574249eca3d41f9135458dfda2762c8',
'dart_webdev_rev': '5f30c560dc4e3df341356c43ec1a766ee6b74a7c',
'dart_webkit_inspection_protocol_rev': 'b459c427b74bf5e0919a083a97a167fb74d8bff1',
- 'dart_yaml_edit_rev': '5c54d455f272bbb83c948ac420c677371e69ae77',
+ 'dart_yaml_edit_rev': '35f4248c7bbba289b3899fa55486e2f31ef1a8c5',
'ocmock_rev': 'c4ec0e3a7a9f56cfdbd0aa01f4f97bb4b75c5ef8', # v3.7.1
@@ -343,16 +343,16 @@
# WARNING: Unused Dart dependencies in the list below till "WARNING:" marker are removed automatically - see create_updated_flutter_deps.py.
'src/flutter/third_party/dart/third_party/binaryen/src':
- Var('chromium_git') + '/external/github.com/WebAssembly/binaryen.git@459bc0797f67cb2a8fd4598bb7143b34036608d9',
+ Var('chromium_git') + '/external/github.com/WebAssembly/binaryen.git@93883fde36ac158fd415dcd6dbd387dcfd928d3c',
'src/flutter/third_party/dart/third_party/devtools':
- {'dep_type': 'cipd', 'packages': [{'package': 'dart/third_party/flutter/devtools', 'version': 'git_revision:f5e84f91b32b219d646cfb87a891cd143dc84056'}]},
+ {'dep_type': 'cipd', 'packages': [{'package': 'dart/third_party/flutter/devtools', 'version': 'git_revision:dcef4f6efe986f110f55dbbe7f3731802e86690a'}]},
'src/flutter/third_party/dart/third_party/pkg/args':
- Var('dart_git') + '/args.git@e623652744c82533829f2e62b1aba1a6cf06e291',
+ Var('dart_git') + '/args.git@09c0fca1785c9df39288a48f767994eed80bed40',
'src/flutter/third_party/dart/third_party/pkg/async':
- Var('dart_git') + '/async.git@c0d81f8699682d01d657a9bf827107d11904a247',
+ Var('dart_git') + '/async.git@5f70a996f673d625e3502597084653686c3e754c',
'src/flutter/third_party/dart/third_party/pkg/bazel_worker':
Var('dart_git') + '/bazel_worker.git@aa3cc9e826350b960e0c5a67e6065bcedba8b0ac',
@@ -373,10 +373,10 @@
Var('dart_git') + '/collection.git' + '@' + Var('dart_collection_rev'),
'src/flutter/third_party/dart/third_party/pkg/convert':
- Var('dart_git') + '/convert.git@9035cafefc1da4315f26058734d0c2a19d5ab56a',
+ Var('dart_git') + '/convert.git@d361833e117cb2438d2a2a6d0b0acb28ff0910fb',
'src/flutter/third_party/dart/third_party/pkg/crypto':
- Var('dart_git') + '/crypto.git@eede7d6918c51159c1422b7449f40dbac660ee57',
+ Var('dart_git') + '/crypto.git@3d26ef4cf22d4b218ba30e616544ad3cf52f64a1',
'src/flutter/third_party/dart/third_party/pkg/csslib':
Var('dart_git') + '/csslib.git@a3700b05bbcc42782e8a7024790dbf019d89c249',
@@ -385,7 +385,7 @@
Var('dart_git') + '/dart_style.git@5d35f4d829ffb8532d345d95d3e9504ae6cd839e',
'src/flutter/third_party/dart/third_party/pkg/dartdoc':
- Var('dart_git') + '/dartdoc.git@5df03dd913a0a2e20421cac61112aa84111160e0',
+ Var('dart_git') + '/dartdoc.git@80c6f18f34b387d4b9ce89ddd2e3049093335f9d',
'src/flutter/third_party/dart/third_party/pkg/file':
Var('dart_git') + '/external/github.com/google/file.dart@6842feaef1c4e06239bd30f8d3ef722838b1c97e',
@@ -406,13 +406,13 @@
Var('dart_git') + '/http_multi_server.git@e7515b5896b83d522189802a1e14e103e19426c0',
'src/flutter/third_party/dart/third_party/pkg/http_parser':
- Var('dart_git') + '/http_parser.git@ce528cf82f3d26ac761e29b2494a9e0c270d4939',
+ Var('dart_git') + '/http_parser.git@23d775898ee90be9daf3297e298a8869bc755d84',
'src/flutter/third_party/dart/third_party/pkg/intl':
Var('dart_git') + '/intl.git@5d65e3808ce40e6282e40881492607df4e35669f',
'src/flutter/third_party/dart/third_party/pkg/json_rpc_2':
- Var('dart_git') + '/json_rpc_2.git@b4810dc7bee5828f240586c81f3f34853cacdbce',
+ Var('dart_git') + '/json_rpc_2.git@c9b616bded8cdb5bfdc836ba7648afa6aba40062',
'src/flutter/third_party/dart/third_party/pkg/leak_tracker':
Var('dart_git') + '/leak_tracker.git@f5620600a5ce1c44f65ddaa02001e200b096e14c',
@@ -427,7 +427,7 @@
Var('dart_git') + '/matcher.git@31f13583630e093731c8cf2b843c14196d748c5c',
'src/flutter/third_party/dart/third_party/pkg/mockito':
- Var('dart_git') + '/mockito.git@3de67548e833a8eef66a2a49070b197c2c08b3ab',
+ Var('dart_git') + '/mockito.git@57d484f9b8e7f6a504966a901174358a42fa932a',
'src/flutter/third_party/dart/third_party/pkg/native':
Var('dart_git') + '/native.git@659511886501bcce638c3966590df04984909ef0',
@@ -439,7 +439,7 @@
Var('dart_git') + '/path.git@e969f42ed112dd702a9453beb9df6c12ae2d3805',
'src/flutter/third_party/dart/third_party/pkg/pool':
- Var('dart_git') + '/pool.git@924fb04353cec915d927f9f1aed88e2eda92b98a',
+ Var('dart_git') + '/pool.git@7bfc71b39742753a88688e56e55a828a2f5dc0bf',
'src/flutter/third_party/dart/third_party/pkg/protobuf':
Var('dart_git') + '/protobuf.git' + '@' + Var('dart_protobuf_rev'),
@@ -469,10 +469,10 @@
Var('dart_git') + '/stream_channel.git@f4407168b275fcde9187baefd7dbce76d0992825',
'src/flutter/third_party/dart/third_party/pkg/string_scanner':
- Var('dart_git') + '/string_scanner.git@2139417ffcd0392bde3ba9bc83ee13eaa5fbed01',
+ Var('dart_git') + '/string_scanner.git@084b201c54b168aced178fff41fce71e3869ae42',
'src/flutter/third_party/dart/third_party/pkg/tar':
- Var('dart_git') + '/external/github.com/simolus3/tar.git@32ceb55e673141abff4e84b99483fe5eb881c291',
+ Var('dart_git') + '/external/github.com/simolus3/tar.git@5a1ea943e70cdf3fa5e1102cdbb9418bd9b4b81a',
'src/flutter/third_party/dart/third_party/pkg/term_glyph':
Var('dart_git') + '/term_glyph.git@19d8c08a4e81122639129c62049896021910c932',
@@ -487,7 +487,7 @@
Var('dart_git') + '/tools.git' + '@' + Var('dart_tools_rev'),
'src/flutter/third_party/dart/third_party/pkg/typed_data':
- Var('dart_git') + '/typed_data.git@2bb9e6ead6394e2d4ec6068c5ece8b2ec0e2b945',
+ Var('dart_git') + '/typed_data.git@6abfafdcf661cd8a814619d7e2a3e99edb3a3862',
'src/flutter/third_party/dart/third_party/pkg/watcher':
Var('dart_git') + '/watcher.git' + '@' + Var('dart_watcher_rev'),
@@ -496,7 +496,7 @@
Var('dart_git') + '/web.git' + '@' + Var('dart_web_rev'),
'src/flutter/third_party/dart/third_party/pkg/web_socket_channel':
- Var('dart_git') + '/web_socket_channel.git@0e1d6e2eb5a0bfd62e45b772ac7107d796176cf6',
+ Var('dart_git') + '/web_socket_channel.git@40aa29f1d2167467f5934d755891a8beb62a1239',
'src/flutter/third_party/dart/third_party/pkg/webdev':
Var('dart_git') + '/webdev.git' + '@' + Var('dart_webdev_rev'),
@@ -956,7 +956,7 @@
'packages': [
{
'package': 'fuchsia/sdk/core/linux-amd64',
- 'version': 'T2Cq00sVabK2fCW9rsjkaWqRxX6RLvKGTNya2LeZ2MAC'
+ 'version': '9F_NaKPd2twhbPwP7A4a6UlWT-_f9PVyLrS8ZFo-3UkC'
}
],
'condition': 'download_fuchsia_deps and not download_fuchsia_sdk',
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 771f879..248a9f2 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -212,7 +212,9 @@
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_overrides
- - unnecessary_parenthesis
+ # TODO(zanderso): Temporarily disabling to unblock a Dart -> Engine roll.
+ # https://github.com/flutter/engine/pull/55927.
+ # - unnecessary_parenthesis
# - unnecessary_raw_strings # what's "necessary" is a matter of opinion; consistency across strings can help readability more than this lint
- unnecessary_statements
- unnecessary_string_escapes
diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files
index b70659a..993fc88 100644
--- a/ci/licenses_golden/excluded_files
+++ b/ci/licenses_golden/excluded_files
@@ -1444,6 +1444,11 @@
../../../flutter/third_party/dart/runtime/tools/heapsnapshot/README.md
../../../flutter/third_party/dart/runtime/tools/heapsnapshot/pubspec.yaml
../../../flutter/third_party/dart/runtime/tools/heapsnapshot/test
+../../../flutter/third_party/dart/runtime/tools/profiling/.gitignore
+../../../flutter/third_party/dart/runtime/tools/profiling/CHANGELOG.md
+../../../flutter/third_party/dart/runtime/tools/profiling/README.md
+../../../flutter/third_party/dart/runtime/tools/profiling/analysis_options.yaml
+../../../flutter/third_party/dart/runtime/tools/profiling/pubspec.yaml
../../../flutter/third_party/dart/runtime/tools/utils.py
../../../flutter/third_party/dart/runtime/tools/valgrind.py
../../../flutter/third_party/dart/runtime/tools/wiki/README.md
@@ -1490,6 +1495,7 @@
../../../flutter/third_party/dart/runtime/vm/compiler/backend/locations_helpers_test.cc
../../../flutter/third_party/dart/runtime/vm/compiler/backend/loops_test.cc
../../../flutter/third_party/dart/runtime/vm/compiler/backend/memory_copy_test.cc
+../../../flutter/third_party/dart/runtime/vm/compiler/backend/pragma_unsafe_no_bounds_check_test.cc
../../../flutter/third_party/dart/runtime/vm/compiler/backend/range_analysis_test.cc
../../../flutter/third_party/dart/runtime/vm/compiler/backend/reachability_fence_test.cc
../../../flutter/third_party/dart/runtime/vm/compiler/backend/redundancy_elimination_test.cc
@@ -1565,7 +1571,7 @@
../../../flutter/third_party/dart/runtime/vm/port_test.cc
../../../flutter/third_party/dart/runtime/vm/profiler_test.cc
../../../flutter/third_party/dart/runtime/vm/protos/.gitignore
-../../../flutter/third_party/dart/runtime/vm/regexp_test.cc
+../../../flutter/third_party/dart/runtime/vm/regexp/regexp_test.cc
../../../flutter/third_party/dart/runtime/vm/ring_buffer_test.cc
../../../flutter/third_party/dart/runtime/vm/scopes_test.cc
../../../flutter/third_party/dart/runtime/vm/service
diff --git a/ci/licenses_golden/licenses_dart b/ci/licenses_golden/licenses_dart
index 6ec669e..7d85232 100644
--- a/ci/licenses_golden/licenses_dart
+++ b/ci/licenses_golden/licenses_dart
@@ -1,7 +1,8 @@
-Signature: 125335ea80d045b9f6f1a902f51abbc7
+Signature: 29033265e95e2a79cca60a10ae90b328
====================================================================================================
LIBRARY: dart
+ORIGIN: http://www.apache.org/licenses/LICENSE-2.0 referenced by ../../../flutter/third_party/dart/runtime/tools/profiling/lib/src/pprof/profile.proto
ORIGIN: http://www.apache.org/licenses/LICENSE-2.0 referenced by ../../../flutter/third_party/dart/runtime/vm/protos/perfetto/common/builtin_clock.proto
ORIGIN: http://www.apache.org/licenses/LICENSE-2.0 referenced by ../../../flutter/third_party/dart/runtime/vm/protos/perfetto/trace/clock_snapshot.proto
ORIGIN: http://www.apache.org/licenses/LICENSE-2.0 referenced by ../../../flutter/third_party/dart/runtime/vm/protos/perfetto/trace/interned_data/interned_data.proto
@@ -15,6 +16,7 @@
ORIGIN: http://www.apache.org/licenses/LICENSE-2.0 referenced by ../../../flutter/third_party/dart/runtime/vm/protos/perfetto/trace/track_event/track_descriptor.proto
ORIGIN: http://www.apache.org/licenses/LICENSE-2.0 referenced by ../../../flutter/third_party/dart/runtime/vm/protos/perfetto/trace/track_event/track_event.proto
TYPE: LicenseType.apache
+FILE: ../../../flutter/third_party/dart/runtime/tools/profiling/lib/src/pprof/profile.proto
FILE: ../../../flutter/third_party/dart/runtime/vm/protos/perfetto/common/builtin_clock.proto
FILE: ../../../flutter/third_party/dart/runtime/vm/protos/perfetto/trace/clock_snapshot.proto
FILE: ../../../flutter/third_party/dart/runtime/vm/protos/perfetto/trace/interned_data/interned_data.proto
@@ -1924,16 +1926,19 @@
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/metrics.h + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/object_graph.cc + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/object_graph.h + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp.cc + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp.h + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp_assembler.cc + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp_assembler.h + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp_assembler_ir.cc + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp_assembler_ir.h + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp_ast.cc + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp_ast.h + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp_parser.cc + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp_parser.h + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp.cc + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp.h + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_assembler.cc + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_assembler.h + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_assembler_ir.cc + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_assembler_ir.h + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_ast.cc + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_ast.h + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_parser.cc + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_parser.h + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/unibrow-inl.h + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/unibrow.cc + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/unibrow.h + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/report.cc + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/report.h + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/ring_buffer.h + ../../../flutter/third_party/dart/LICENSE
@@ -1942,9 +1947,6 @@
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/simulator_arm64.h + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/stack_frame_arm64.h + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/tags.cc + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/unibrow-inl.h + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/unibrow.cc + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/unibrow.h + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/sdk/lib/_internal/js_dev_runtime/private/preambles/d8.js + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/sdk/lib/_internal/js_dev_runtime/private/preambles/jsshell.js + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/sdk/lib/_internal/js_runtime/lib/linked_hash_map.dart + ../../../flutter/third_party/dart/LICENSE
@@ -2016,16 +2018,19 @@
FILE: ../../../flutter/third_party/dart/runtime/vm/metrics.h
FILE: ../../../flutter/third_party/dart/runtime/vm/object_graph.cc
FILE: ../../../flutter/third_party/dart/runtime/vm/object_graph.h
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp.cc
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp.h
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp_assembler.cc
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp_assembler.h
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp_assembler_ir.cc
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp_assembler_ir.h
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp_ast.cc
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp_ast.h
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp_parser.cc
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp_parser.h
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp.cc
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp.h
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_assembler.cc
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_assembler.h
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_assembler_ir.cc
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_assembler_ir.h
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_ast.cc
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_ast.h
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_parser.cc
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_parser.h
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/unibrow-inl.h
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/unibrow.cc
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/unibrow.h
FILE: ../../../flutter/third_party/dart/runtime/vm/report.cc
FILE: ../../../flutter/third_party/dart/runtime/vm/report.h
FILE: ../../../flutter/third_party/dart/runtime/vm/ring_buffer.h
@@ -2034,9 +2039,6 @@
FILE: ../../../flutter/third_party/dart/runtime/vm/simulator_arm64.h
FILE: ../../../flutter/third_party/dart/runtime/vm/stack_frame_arm64.h
FILE: ../../../flutter/third_party/dart/runtime/vm/tags.cc
-FILE: ../../../flutter/third_party/dart/runtime/vm/unibrow-inl.h
-FILE: ../../../flutter/third_party/dart/runtime/vm/unibrow.cc
-FILE: ../../../flutter/third_party/dart/runtime/vm/unibrow.h
FILE: ../../../flutter/third_party/dart/sdk/lib/_internal/js_dev_runtime/private/preambles/d8.js
FILE: ../../../flutter/third_party/dart/sdk/lib/_internal/js_dev_runtime/private/preambles/jsshell.js
FILE: ../../../flutter/third_party/dart/sdk/lib/_internal/js_runtime/lib/linked_hash_map.dart
@@ -2116,12 +2118,12 @@
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/profiler_service.h + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/program_visitor.cc + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/program_visitor.h + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp_assembler_bytecode.cc + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp_assembler_bytecode.h + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp_assembler_bytecode_inl.h + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp_bytecodes.h + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp_interpreter.cc + ../../../flutter/third_party/dart/LICENSE
-ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp_interpreter.h + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_assembler_bytecode.cc + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_assembler_bytecode.h + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_assembler_bytecode_inl.h + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_bytecodes.h + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_interpreter.cc + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_interpreter.h + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/scope_timer.h + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/service_event.cc + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/service_event.h + ../../../flutter/third_party/dart/LICENSE
@@ -2195,12 +2197,12 @@
FILE: ../../../flutter/third_party/dart/runtime/vm/profiler_service.h
FILE: ../../../flutter/third_party/dart/runtime/vm/program_visitor.cc
FILE: ../../../flutter/third_party/dart/runtime/vm/program_visitor.h
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp_assembler_bytecode.cc
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp_assembler_bytecode.h
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp_assembler_bytecode_inl.h
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp_bytecodes.h
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp_interpreter.cc
-FILE: ../../../flutter/third_party/dart/runtime/vm/regexp_interpreter.h
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_assembler_bytecode.cc
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_assembler_bytecode.h
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_assembler_bytecode_inl.h
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_bytecodes.h
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_interpreter.cc
+FILE: ../../../flutter/third_party/dart/runtime/vm/regexp/regexp_interpreter.h
FILE: ../../../flutter/third_party/dart/runtime/vm/scope_timer.h
FILE: ../../../flutter/third_party/dart/runtime/vm/service_event.cc
FILE: ../../../flutter/third_party/dart/runtime/vm/service_event.h
@@ -4074,6 +4076,11 @@
ORIGIN: ../../../flutter/third_party/dart/runtime/platform/synchronization_win.cc + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/platform/threads.h + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/tools/dartfuzz/flag_fuzzer.dart + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/tools/profiling/bin/convert_allocation_profile.dart + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/tools/profiling/bin/set_uprobe.dart + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/tools/profiling/lib/src/elf_utils.dart + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/tools/profiling/lib/src/perf/perf_data.dart + ../../../flutter/third_party/dart/LICENSE
+ORIGIN: ../../../flutter/third_party/dart/runtime/tools/profiling/lib/src/symbols.dart + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/bytecode_reader.cc + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/bytecode_reader.h + ../../../flutter/third_party/dart/LICENSE
ORIGIN: ../../../flutter/third_party/dart/runtime/vm/compiler/assembler/disassembler_kbc.cc + ../../../flutter/third_party/dart/LICENSE
@@ -4120,6 +4127,11 @@
FILE: ../../../flutter/third_party/dart/runtime/platform/synchronization_win.cc
FILE: ../../../flutter/third_party/dart/runtime/platform/threads.h
FILE: ../../../flutter/third_party/dart/runtime/tools/dartfuzz/flag_fuzzer.dart
+FILE: ../../../flutter/third_party/dart/runtime/tools/profiling/bin/convert_allocation_profile.dart
+FILE: ../../../flutter/third_party/dart/runtime/tools/profiling/bin/set_uprobe.dart
+FILE: ../../../flutter/third_party/dart/runtime/tools/profiling/lib/src/elf_utils.dart
+FILE: ../../../flutter/third_party/dart/runtime/tools/profiling/lib/src/perf/perf_data.dart
+FILE: ../../../flutter/third_party/dart/runtime/tools/profiling/lib/src/symbols.dart
FILE: ../../../flutter/third_party/dart/runtime/vm/bytecode_reader.cc
FILE: ../../../flutter/third_party/dart/runtime/vm/bytecode_reader.h
FILE: ../../../flutter/third_party/dart/runtime/vm/compiler/assembler/disassembler_kbc.cc
@@ -4371,6 +4383,10 @@
FILE: ../../../flutter/third_party/dart/runtime/tools/entitlements/gen_snapshot.plist
FILE: ../../../flutter/third_party/dart/runtime/tools/entitlements/gen_snapshot_product.plist
FILE: ../../../flutter/third_party/dart/runtime/tools/entitlements/run_vm_tests.plist
+FILE: ../../../flutter/third_party/dart/runtime/tools/profiling/lib/src/pprof/generated/profile.pb.dart
+FILE: ../../../flutter/third_party/dart/runtime/tools/profiling/lib/src/pprof/generated/profile.pbenum.dart
+FILE: ../../../flutter/third_party/dart/runtime/tools/profiling/lib/src/pprof/generated/profile.pbjson.dart
+FILE: ../../../flutter/third_party/dart/runtime/tools/profiling/lib/src/pprof/generated/profile.pbserver.dart
FILE: ../../../flutter/third_party/dart/runtime/tools/wiki/styles/style.scss
FILE: ../../../flutter/third_party/dart/runtime/tools/wiki/templates/includes/auto-refresh.html
FILE: ../../../flutter/third_party/dart/runtime/tools/wiki/templates/page.html
@@ -4787,7 +4803,7 @@
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
-You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/115a3c609bf22d78d532ea9a9ecbcfb11ca9764f
+You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/993d3069f42e98b2b29e441bc98424065cc255ca
/third_party/fallback_root_certificates/
====================================================================================================
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index fd9a96d..283a93a 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -44799,6 +44799,9 @@
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_layout.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_layout.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_layout_test.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_manager.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_manager.h + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_manager_test.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_pending_event.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_pending_event.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_view_delegate.cc + ../../../flutter/LICENSE
@@ -47699,6 +47702,9 @@
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_layout.cc
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_layout.h
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_layout_test.cc
+FILE: ../../../flutter/shell/platform/linux/fl_keyboard_manager.cc
+FILE: ../../../flutter/shell/platform/linux/fl_keyboard_manager.h
+FILE: ../../../flutter/shell/platform/linux/fl_keyboard_manager_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_pending_event.cc
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_pending_event.h
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_view_delegate.cc
diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia
index d4b5ecb..4d22975 100644
--- a/ci/licenses_golden/licenses_fuchsia
+++ b/ci/licenses_golden/licenses_fuchsia
@@ -1,4 +1,4 @@
-Signature: 147bb5b1f6ce1d858d13c192ce702004
+Signature: e6795c8a2a5ff5824f7e5b3c503cf201
====================================================================================================
LIBRARY: fuchsia_sdk
@@ -14859,7 +14859,6 @@
ORIGIN: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/exception.h + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/functional.h + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/span.h + ../../../fuchsia/sdk/linux/LICENSE
-ORIGIN: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/tuple.h + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/type_traits.h + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/iterator.h + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/span.h + ../../../fuchsia/sdk/linux/LICENSE
@@ -15042,7 +15041,6 @@
FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/exception.h
FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/functional.h
FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/span.h
-FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/tuple.h
FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/type_traits.h
FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/iterator.h
FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/span.h
@@ -15415,7 +15413,6 @@
ORIGIN: ../../../fuchsia/sdk/linux/pkg/scenic_cpp_testing/fake_flatland.cc + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/scenic_cpp_testing/fake_flatland_types.cc + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/linkage.h + ../../../fuchsia/sdk/linux/LICENSE
-ORIGIN: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/variant.h + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/sys/component/realm_builder_absolute.shard.cml + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/sys/component/realm_builder_base.shard.cml + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/sys_component_cpp_testing/internal/convert.cc + ../../../fuchsia/sdk/linux/LICENSE
@@ -15769,7 +15766,6 @@
FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp_testing/fake_flatland.cc
FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp_testing/fake_flatland_types.cc
FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/linkage.h
-FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/variant.h
FILE: ../../../fuchsia/sdk/linux/pkg/sys/component/realm_builder_absolute.shard.cml
FILE: ../../../fuchsia/sdk/linux/pkg/sys/component/realm_builder_base.shard.cml
FILE: ../../../fuchsia/sdk/linux/pkg/sys_component_cpp_testing/internal/convert.cc
@@ -16126,6 +16122,8 @@
ORIGIN: ../../../fuchsia/sdk/linux/obj/x64-api-23/sysroot/include/zircon/availability_levels.inc + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/obj/x64-api-24/sysroot/include/zircon/availability_levels.inc + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/obj/x64-api-NEXT/sysroot/include/zircon/availability_levels.inc + ../../../fuchsia/sdk/linux/LICENSE
+ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_incoming_cpp/include/lib/driver/incoming/cpp/service_validator.h + ../../../fuchsia/sdk/linux/LICENSE
+ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_incoming_cpp/service_validator.cc + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_metadata_cpp/include/lib/driver/metadata/cpp/metadata.h + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_metadata_cpp/include/lib/driver/metadata/cpp/metadata_server.h + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/driver_node_cpp/add_child.cc + ../../../fuchsia/sdk/linux/LICENSE
@@ -16148,6 +16146,7 @@
ORIGIN: ../../../fuchsia/sdk/linux/pkg/syslog_cpp/include/lib/syslog/cpp/log_message_impl.h + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/syslog_cpp/log_message_impl.cc + ../../../fuchsia/sdk/linux/LICENSE
ORIGIN: ../../../fuchsia/sdk/linux/pkg/vulkan/offer.shard.cml + ../../../fuchsia/sdk/linux/LICENSE
+ORIGIN: ../../../fuchsia/sdk/linux/pkg/zx/time.cc + ../../../fuchsia/sdk/linux/LICENSE
TYPE: LicenseType.bsd
FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/availability_levels.inc
FILE: ../../../fuchsia/sdk/linux/arch/riscv64/sysroot/include/zircon/availability_levels.inc
@@ -16208,6 +16207,8 @@
FILE: ../../../fuchsia/sdk/linux/obj/x64-api-23/sysroot/include/zircon/availability_levels.inc
FILE: ../../../fuchsia/sdk/linux/obj/x64-api-24/sysroot/include/zircon/availability_levels.inc
FILE: ../../../fuchsia/sdk/linux/obj/x64-api-NEXT/sysroot/include/zircon/availability_levels.inc
+FILE: ../../../fuchsia/sdk/linux/pkg/driver_incoming_cpp/include/lib/driver/incoming/cpp/service_validator.h
+FILE: ../../../fuchsia/sdk/linux/pkg/driver_incoming_cpp/service_validator.cc
FILE: ../../../fuchsia/sdk/linux/pkg/driver_metadata_cpp/include/lib/driver/metadata/cpp/metadata.h
FILE: ../../../fuchsia/sdk/linux/pkg/driver_metadata_cpp/include/lib/driver/metadata/cpp/metadata_server.h
FILE: ../../../fuchsia/sdk/linux/pkg/driver_node_cpp/add_child.cc
@@ -16230,6 +16231,7 @@
FILE: ../../../fuchsia/sdk/linux/pkg/syslog_cpp/include/lib/syslog/cpp/log_message_impl.h
FILE: ../../../fuchsia/sdk/linux/pkg/syslog_cpp/log_message_impl.cc
FILE: ../../../fuchsia/sdk/linux/pkg/vulkan/offer.shard.cml
+FILE: ../../../fuchsia/sdk/linux/pkg/zx/time.cc
----------------------------------------------------------------------------------------------------
Copyright 2024 The Fuchsia Authors. All rights reserved.
diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia
index 844c362..a96a0bb 100644
--- a/ci/licenses_golden/licenses_skia
+++ b/ci/licenses_golden/licenses_skia
@@ -1,4 +1,4 @@
-Signature: 015672489e829391bd5d2de2efd67231
+Signature: 83c4ecafc6e635226445e328864ab843
====================================================================================================
LIBRARY: etc1
diff --git a/impeller/core/device_buffer.h b/impeller/core/device_buffer.h
index 07dba77..7fa2f3e 100644
--- a/impeller/core/device_buffer.h
+++ b/impeller/core/device_buffer.h
@@ -23,9 +23,9 @@
Range source_range,
size_t offset = 0u);
- virtual bool SetLabel(const std::string& label) = 0;
+ virtual bool SetLabel(std::string_view label) = 0;
- virtual bool SetLabel(const std::string& label, Range range) = 0;
+ virtual bool SetLabel(std::string_view label, Range range) = 0;
/// @brief Create a buffer view of this entire buffer.
static BufferView AsBufferView(std::shared_ptr<DeviceBuffer> buffer);
diff --git a/impeller/core/host_buffer.cc b/impeller/core/host_buffer.cc
index 69b8c1c..1bee375 100644
--- a/impeller/core/host_buffer.cc
+++ b/impeller/core/host_buffer.cc
@@ -37,10 +37,6 @@
HostBuffer::~HostBuffer() = default;
-void HostBuffer::SetLabel(std::string label) {
- label_ = std::move(label);
-}
-
BufferView HostBuffer::Emplace(const void* buffer,
size_t length,
size_t align) {
diff --git a/impeller/core/host_buffer.h b/impeller/core/host_buffer.h
index 90ab30d..43c03c3 100644
--- a/impeller/core/host_buffer.h
+++ b/impeller/core/host_buffer.h
@@ -30,10 +30,7 @@
static std::shared_ptr<HostBuffer> Create(
const std::shared_ptr<Allocator>& allocator);
- // |Buffer|
- virtual ~HostBuffer();
-
- void SetLabel(std::string label);
+ ~HostBuffer();
//----------------------------------------------------------------------------
/// @brief Emplace uniform data onto the host buffer. Ensure that backend
@@ -169,7 +166,6 @@
size_t current_buffer_ = 0u;
size_t offset_ = 0u;
size_t frame_index_ = 0u;
- std::string label_;
};
} // namespace impeller
diff --git a/impeller/display_list/aiks_dl_basic_unittests.cc b/impeller/display_list/aiks_dl_basic_unittests.cc
index 4dcfc75..39e5d05 100644
--- a/impeller/display_list/aiks_dl_basic_unittests.cc
+++ b/impeller/display_list/aiks_dl_basic_unittests.cc
@@ -94,7 +94,7 @@
namespace {
bool GenerateMipmap(const std::shared_ptr<Context>& context,
std::shared_ptr<Texture> texture,
- std::string label) {
+ std::string_view label) {
auto buffer = context->CreateCommandBuffer();
if (!buffer) {
return false;
@@ -103,7 +103,7 @@
if (!pass) {
return false;
}
- pass->GenerateMipmap(std::move(texture), std::move(label));
+ pass->GenerateMipmap(std::move(texture), label);
pass->EncodeCommands(context->GetResourceAllocator());
return context->GetCommandQueue()->Submit({buffer}).ok();
diff --git a/impeller/display_list/aiks_dl_blend_unittests.cc b/impeller/display_list/aiks_dl_blend_unittests.cc
index 41c57e8..f4691f6 100644
--- a/impeller/display_list/aiks_dl_blend_unittests.cc
+++ b/impeller/display_list/aiks_dl_blend_unittests.cc
@@ -888,5 +888,22 @@
ASSERT_TRUE(OpenPlaygroundHere(callback));
}
+TEST_P(AiksTest, DestructiveBlendColorFilterFloodsClip) {
+ DisplayListBuilder builder;
+
+ DlPaint paint;
+ paint.setColor(DlColor::kBlue());
+ builder.DrawPaint(paint);
+
+ DlPaint save_paint;
+ save_paint.setColorFilter(
+ DlBlendColorFilter::Make(DlColor::kRed(), DlBlendMode::kSrc));
+ builder.SaveLayer(nullptr, &save_paint);
+ builder.Restore();
+
+ // Should be solid red as the destructive color filter floods the clip.
+ ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
+}
+
} // namespace testing
} // namespace impeller
diff --git a/impeller/display_list/aiks_dl_unittests.cc b/impeller/display_list/aiks_dl_unittests.cc
index 236345f..279b7bc 100644
--- a/impeller/display_list/aiks_dl_unittests.cc
+++ b/impeller/display_list/aiks_dl_unittests.cc
@@ -160,12 +160,15 @@
paint.setColor(DlColor::kBlack().withAlpha(128));
paint.setColorFilter(
DlBlendColorFilter::Make(DlColor::kRed(), DlBlendMode::kDstOver));
+ builder.Save();
+ builder.ClipRect(SkRect::MakeXYWH(100, 500, 300, 300));
builder.SaveLayer(nullptr, &paint);
DlPaint draw_paint;
draw_paint.setColor(DlColor::kBlue());
builder.DrawRect(SkRect::MakeXYWH(100, 500, 300, 300), draw_paint);
builder.Restore();
+ builder.Restore();
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
@@ -203,12 +206,15 @@
save_paint.setColor(DlColor::kBlack().withAlpha(128));
save_paint.setColorFilter(
DlBlendColorFilter::Make(DlColor::kRed(), DlBlendMode::kDstOver));
+ builder.Save();
+ builder.ClipRect(SkRect::MakeXYWH(100, 500, 300, 300));
builder.SaveLayer(nullptr, &save_paint);
DlPaint draw_paint;
draw_paint.setColor(DlColor::kBlue());
builder.DrawRect(SkRect::MakeXYWH(100, 500, 300, 300), draw_paint);
builder.Restore();
+ builder.Restore();
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
diff --git a/impeller/display_list/canvas.cc b/impeller/display_list/canvas.cc
index d14db69..dedb9c3 100644
--- a/impeller/display_list/canvas.cc
+++ b/impeller/display_list/canvas.cc
@@ -1015,7 +1015,9 @@
filter_contents, //
/*flood_output_coverage=*/
Entity::IsBlendModeDestructive(paint.blend_mode), //
- /*flood_input_coverage=*/!!backdrop_filter //
+ /*flood_input_coverage=*/!!backdrop_filter ||
+ (paint.color_filter &&
+ paint.color_filter->modifies_transparent_black()) //
);
if (!maybe_subpass_coverage.has_value()) {
diff --git a/impeller/entity/contents/atlas_contents.cc b/impeller/entity/contents/atlas_contents.cc
index 19053b0..3addc0b 100644
--- a/impeller/entity/contents/atlas_contents.cc
+++ b/impeller/entity/contents/atlas_contents.cc
@@ -94,8 +94,7 @@
using FS = PorterDuffBlendPipeline::FragmentShader;
#ifdef IMPELLER_DEBUG
- pass.SetCommandLabel(
- SPrintF("DrawAtlas Blend (%s)", BlendModeToString(blend_mode)));
+ pass.SetCommandLabel("DrawAtlas Blend");
#endif // IMPELLER_DEBUG
pass.SetVertexBuffer(geometry_->CreateBlendVertexBuffer(host_buffer));
pass.SetPipeline(
@@ -138,8 +137,7 @@
using FS = VerticesUberShader::FragmentShader;
#ifdef IMPELLER_DEBUG
- pass.SetCommandLabel(
- SPrintF("DrawAtlas Advanced Blend (%s)", BlendModeToString(blend_mode)));
+ pass.SetCommandLabel("DrawAtlas Advanced Blend");
#endif // IMPELLER_DEBUG
pass.SetVertexBuffer(geometry_->CreateBlendVertexBuffer(host_buffer));
diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc
index a563c2c..70d1df7 100644
--- a/impeller/entity/contents/content_context.cc
+++ b/impeller/entity/contents/content_context.cc
@@ -493,12 +493,12 @@
if (context->GetCapabilities()->SupportsOffscreenMSAA() && msaa_enabled) {
subpass_target = GetRenderTargetCache()->CreateOffscreenMSAA(
*context, texture_size,
- /*mip_count=*/mip_count, SPrintF("%s Offscreen", label.data()),
+ /*mip_count=*/mip_count, label,
RenderTarget::kDefaultColorAttachmentConfigMSAA, depth_stencil_config);
} else {
subpass_target = GetRenderTargetCache()->CreateOffscreen(
*context, texture_size,
- /*mip_count=*/mip_count, SPrintF("%s Offscreen", label.data()),
+ /*mip_count=*/mip_count, label,
RenderTarget::kDefaultColorAttachmentConfig, depth_stencil_config);
}
return MakeSubpass(label, subpass_target, command_buffer, subpass_callback);
@@ -520,7 +520,7 @@
if (!sub_renderpass) {
return fml::Status(fml::StatusCode::kUnknown, "");
}
- sub_renderpass->SetLabel(SPrintF("%s RenderPass", label.data()));
+ sub_renderpass->SetLabel(label);
if (!subpass_callback(*this, *sub_renderpass)) {
return fml::Status(fml::StatusCode::kUnknown, "");
diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h
index c2fe1fc..9abdc51 100644
--- a/impeller/entity/contents/content_context.h
+++ b/impeller/entity/contents/content_context.h
@@ -992,7 +992,7 @@
PipelineDescriptor& desc) {
opts.ApplyToPipelineDescriptor(desc);
desc.SetLabel(
- SPrintF("%s V#%zu", desc.GetLabel().c_str(), variants_count));
+ SPrintF("%s V#%zu", desc.GetLabel().data(), variants_count));
});
std::unique_ptr<RenderPipelineHandleT> variant =
std::make_unique<RenderPipelineHandleT>(std::move(variant_future));
diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
index d2e39ea..b70c717 100644
--- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
+++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
@@ -239,7 +239,7 @@
// .Contains(coverage_hint.value()))
std::optional<Rect> snapshot_coverage = input_snapshot.GetCoverage();
- if (input_snapshot.transform.IsIdentity() &&
+ if (input_snapshot.transform.Equals(snapshot_entity.GetTransform()) &&
source_expanded_coverage_hint.has_value() &&
snapshot_coverage.has_value() &&
snapshot_coverage->Contains(source_expanded_coverage_hint.value())) {
diff --git a/impeller/entity/contents/test/recording_render_pass.cc b/impeller/entity/contents/test/recording_render_pass.cc
index dcc35cc..da36a5f 100644
--- a/impeller/entity/contents/test/recording_render_pass.cc
+++ b/impeller/entity/contents/test/recording_render_pass.cc
@@ -92,7 +92,7 @@
}
// |RenderPass|
-void RecordingRenderPass::OnSetLabel(std::string label) {
+void RecordingRenderPass::OnSetLabel(std::string_view label) {
return;
}
diff --git a/impeller/entity/contents/test/recording_render_pass.h b/impeller/entity/contents/test/recording_render_pass.h
index 12fd10c..ffbef6f 100644
--- a/impeller/entity/contents/test/recording_render_pass.h
+++ b/impeller/entity/contents/test/recording_render_pass.h
@@ -69,7 +69,7 @@
const std::unique_ptr<const Sampler>& sampler) override;
// |RenderPass|
- void OnSetLabel(std::string label) override;
+ void OnSetLabel(std::string_view label) override;
// |RenderPass|
bool OnEncodeCommands(const Context& context) const override;
diff --git a/impeller/entity/contents/texture_contents.cc b/impeller/entity/contents/texture_contents.cc
index f23d39f..f443558 100644
--- a/impeller/entity/contents/texture_contents.cc
+++ b/impeller/entity/contents/texture_contents.cc
@@ -31,8 +31,8 @@
return contents;
}
-void TextureContents::SetLabel(std::string label) {
- label_ = std::move(label);
+void TextureContents::SetLabel(std::string_view label) {
+ label_ = label;
}
void TextureContents::SetDestinationRect(Rect rect) {
diff --git a/impeller/entity/contents/texture_contents.h b/impeller/entity/contents/texture_contents.h
index 2526eab..34657ff 100644
--- a/impeller/entity/contents/texture_contents.h
+++ b/impeller/entity/contents/texture_contents.h
@@ -25,7 +25,7 @@
/// when image filters are applied.
static std::shared_ptr<TextureContents> MakeRect(Rect destination);
- void SetLabel(std::string label);
+ void SetLabel(std::string_view label);
void SetDestinationRect(Rect rect);
diff --git a/impeller/entity/render_target_cache.cc b/impeller/entity/render_target_cache.cc
index c96dc6a..dbfe0a9 100644
--- a/impeller/entity/render_target_cache.cc
+++ b/impeller/entity/render_target_cache.cc
@@ -31,7 +31,7 @@
const Context& context,
ISize size,
int mip_count,
- const std::string& label,
+ std::string_view label,
RenderTarget::AttachmentConfig color_attachment_config,
std::optional<RenderTarget::AttachmentConfig> stencil_attachment_config,
const std::shared_ptr<Texture>& existing_color_texture,
@@ -79,7 +79,7 @@
const Context& context,
ISize size,
int mip_count,
- const std::string& label,
+ std::string_view label,
RenderTarget::AttachmentConfigMSAA color_attachment_config,
std::optional<RenderTarget::AttachmentConfig> stencil_attachment_config,
const std::shared_ptr<Texture>& existing_color_msaa_texture,
diff --git a/impeller/entity/render_target_cache.h b/impeller/entity/render_target_cache.h
index 838814e..7c7f914 100644
--- a/impeller/entity/render_target_cache.h
+++ b/impeller/entity/render_target_cache.h
@@ -5,6 +5,7 @@
#ifndef FLUTTER_IMPELLER_ENTITY_RENDER_TARGET_CACHE_H_
#define FLUTTER_IMPELLER_ENTITY_RENDER_TARGET_CACHE_H_
+#include <string_view>
#include "impeller/renderer/render_target.h"
namespace impeller {
@@ -29,7 +30,7 @@
const Context& context,
ISize size,
int mip_count,
- const std::string& label = "Offscreen",
+ std::string_view label = "Offscreen",
RenderTarget::AttachmentConfig color_attachment_config =
RenderTarget::kDefaultColorAttachmentConfig,
std::optional<RenderTarget::AttachmentConfig> stencil_attachment_config =
@@ -42,7 +43,7 @@
const Context& context,
ISize size,
int mip_count,
- const std::string& label = "Offscreen MSAA",
+ std::string_view label = "Offscreen MSAA",
RenderTarget::AttachmentConfigMSAA color_attachment_config =
RenderTarget::kDefaultColorAttachmentConfigMSAA,
std::optional<RenderTarget::AttachmentConfig> stencil_attachment_config =
diff --git a/impeller/entity/save_layer_utils.cc b/impeller/entity/save_layer_utils.cc
index 6c9e528..3049902 100644
--- a/impeller/entity/save_layer_utils.cc
+++ b/impeller/entity/save_layer_utils.cc
@@ -25,11 +25,8 @@
// first is the presence of a backdrop filter on the saveLayer. The second is
// the presence of a color filter that effects transparent black on the
// saveLayer. The last is the presence of unbounded content within the
- // saveLayer (such as a drawPaint, bdf, et cetera). Note that currently
- // only the presence of a backdrop filter impacts this flag, while color
- // filters are not yet handled
- // (https://github.com/flutter/flutter/issues/154035) and unbounded coverage
- // is handled in the display list dispatcher.
+ // saveLayer (such as a drawPaint, bdf, et cetera). Note that unbounded
+ // coverage is handled in the display list dispatcher.
//
// Backdrop filters apply before the saveLayer is restored. The presence of
// a backdrop filter causes the content coverage of the saveLayer to be
diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h
index 1726c30..166fc11 100644
--- a/impeller/geometry/matrix.h
+++ b/impeller/geometry/matrix.h
@@ -407,6 +407,27 @@
std::optional<MatrixDecomposition> Decompose() const;
+ bool Equals(const Matrix& matrix, Scalar epsilon = 1e-5f) const {
+ const Scalar* a = m;
+ const Scalar* b = matrix.m;
+ return ScalarNearlyEqual(a[0], b[0], epsilon) &&
+ ScalarNearlyEqual(a[1], b[1], epsilon) &&
+ ScalarNearlyEqual(a[2], b[2], epsilon) &&
+ ScalarNearlyEqual(a[3], b[3], epsilon) &&
+ ScalarNearlyEqual(a[4], b[4], epsilon) &&
+ ScalarNearlyEqual(a[5], b[5], epsilon) &&
+ ScalarNearlyEqual(a[6], b[6], epsilon) &&
+ ScalarNearlyEqual(a[7], b[7], epsilon) &&
+ ScalarNearlyEqual(a[8], b[8], epsilon) &&
+ ScalarNearlyEqual(a[9], b[9], epsilon) &&
+ ScalarNearlyEqual(a[10], b[10], epsilon) &&
+ ScalarNearlyEqual(a[11], b[11], epsilon) &&
+ ScalarNearlyEqual(a[12], b[12], epsilon) &&
+ ScalarNearlyEqual(a[13], b[13], epsilon) &&
+ ScalarNearlyEqual(a[14], b[14], epsilon) &&
+ ScalarNearlyEqual(a[15], b[15], epsilon);
+ }
+
constexpr bool operator==(const Matrix& m) const {
// clang-format off
return vec[0] == m.vec[0]
diff --git a/impeller/geometry/matrix_unittests.cc b/impeller/geometry/matrix_unittests.cc
index ac16008..91a2a50 100644
--- a/impeller/geometry/matrix_unittests.cc
+++ b/impeller/geometry/matrix_unittests.cc
@@ -25,6 +25,18 @@
11.0, 21.0, 0.0, 1.0)));
}
+TEST(MatrixTest, Equals) {
+ Matrix x;
+ Matrix y = x;
+ EXPECT_TRUE(x.Equals(y));
+}
+
+TEST(MatrixTest, NotEquals) {
+ Matrix x;
+ Matrix y = x.Translate({1, 0, 0});
+ EXPECT_FALSE(x.Equals(y));
+}
+
TEST(MatrixTest, HasPerspective2D) {
EXPECT_FALSE(Matrix().HasPerspective2D());
diff --git a/impeller/geometry/scalar.h b/impeller/geometry/scalar.h
index 2600a49..dadc528 100644
--- a/impeller/geometry/scalar.h
+++ b/impeller/geometry/scalar.h
@@ -22,6 +22,11 @@
return val >= T{} ? val : -val;
}
+template <>
+constexpr Scalar Absolute<Scalar>(const float& val) {
+ return fabsf(val);
+}
+
constexpr inline bool ScalarNearlyZero(Scalar x,
Scalar tolerance = kEhCloseEnough) {
return Absolute(x) <= tolerance;
diff --git a/impeller/renderer/backend/gles/blit_command_gles.cc b/impeller/renderer/backend/gles/blit_command_gles.cc
index 9d20a01..04f6c4a 100644
--- a/impeller/renderer/backend/gles/blit_command_gles.cc
+++ b/impeller/renderer/backend/gles/blit_command_gles.cc
@@ -73,7 +73,7 @@
// glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
// emulate the blit when it's not available in the driver.
if (!gl.BlitFramebuffer.IsAvailable()) {
- // TODO(135818): Emulate the blit using a raster draw call here.
+ // TODO(157064): Emulate the blit using a raster draw call here.
VALIDATION_LOG << "Texture blit fallback not implemented yet for GLES2.";
return false;
}
@@ -367,7 +367,7 @@
// glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
// emulate the blit when it's not available in the driver.
if (!gl.BlitFramebuffer.IsAvailable()) {
- // TODO(135818): Emulate the blit using a raster draw call here.
+ // TODO(157064): Emulate the blit using a raster draw call here.
VALIDATION_LOG << "Texture blit fallback not implemented yet for GLES2.";
return false;
}
diff --git a/impeller/renderer/backend/gles/blit_pass_gles.cc b/impeller/renderer/backend/gles/blit_pass_gles.cc
index 39d963d..94d8300 100644
--- a/impeller/renderer/backend/gles/blit_pass_gles.cc
+++ b/impeller/renderer/backend/gles/blit_pass_gles.cc
@@ -27,8 +27,8 @@
}
// |BlitPass|
-void BlitPassGLES::OnSetLabel(std::string label) {
- label_ = std::move(label);
+void BlitPassGLES::OnSetLabel(std::string_view label) {
+ label_ = std::string(label);
}
[[nodiscard]] bool EncodeCommandsInReactor(
@@ -96,7 +96,7 @@
std::shared_ptr<Texture> destination,
IRect source_region,
IPoint destination_origin,
- std::string label) {
+ std::string_view label) {
auto command = std::make_unique<BlitCopyTextureToTextureCommandGLES>();
command->label = label;
command->source = std::move(source);
@@ -114,7 +114,7 @@
std::shared_ptr<DeviceBuffer> destination,
IRect source_region,
size_t destination_offset,
- std::string label) {
+ std::string_view label) {
auto command = std::make_unique<BlitCopyTextureToBufferCommandGLES>();
command->label = label;
command->source = std::move(source);
@@ -131,7 +131,7 @@
BufferView source,
std::shared_ptr<Texture> destination,
IRect destination_region,
- std::string label,
+ std::string_view label,
uint32_t mip_level,
uint32_t slice,
bool convert_to_read) {
@@ -150,7 +150,7 @@
// |BlitPass|
bool BlitPassGLES::OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
- std::string label) {
+ std::string_view label) {
auto command = std::make_unique<BlitGenerateMipmapCommandGLES>();
command->label = label;
command->texture = std::move(texture);
diff --git a/impeller/renderer/backend/gles/blit_pass_gles.h b/impeller/renderer/backend/gles/blit_pass_gles.h
index f6ad24e..321bd42 100644
--- a/impeller/renderer/backend/gles/blit_pass_gles.h
+++ b/impeller/renderer/backend/gles/blit_pass_gles.h
@@ -35,7 +35,7 @@
bool IsValid() const override;
// |BlitPass|
- void OnSetLabel(std::string label) override;
+ void OnSetLabel(std::string_view label) override;
// |BlitPass|
bool EncodeCommands(
@@ -50,27 +50,27 @@
std::shared_ptr<Texture> destination,
IRect source_region,
IPoint destination_origin,
- std::string label) override;
+ std::string_view label) override;
// |BlitPass|
bool OnCopyTextureToBufferCommand(std::shared_ptr<Texture> source,
std::shared_ptr<DeviceBuffer> destination,
IRect source_region,
size_t destination_offset,
- std::string label) override;
+ std::string_view label) override;
// |BlitPass|
bool OnCopyBufferToTextureCommand(BufferView source,
std::shared_ptr<Texture> destination,
IRect destination_region,
- std::string label,
+ std::string_view label,
uint32_t mip_level,
uint32_t slice,
bool convert_to_read) override;
// |BlitPass|
bool OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
- std::string label) override;
+ std::string_view label) override;
BlitPassGLES(const BlitPassGLES&) = delete;
diff --git a/impeller/renderer/backend/gles/command_buffer_gles.cc b/impeller/renderer/backend/gles/command_buffer_gles.cc
index ea6a651..d570cbc 100644
--- a/impeller/renderer/backend/gles/command_buffer_gles.cc
+++ b/impeller/renderer/backend/gles/command_buffer_gles.cc
@@ -19,7 +19,7 @@
CommandBufferGLES::~CommandBufferGLES() = default;
// |CommandBuffer|
-void CommandBufferGLES::SetLabel(const std::string& label) const {
+void CommandBufferGLES::SetLabel(std::string_view label) const {
// Cannot support.
}
diff --git a/impeller/renderer/backend/gles/command_buffer_gles.h b/impeller/renderer/backend/gles/command_buffer_gles.h
index fe45b92..2188ccd 100644
--- a/impeller/renderer/backend/gles/command_buffer_gles.h
+++ b/impeller/renderer/backend/gles/command_buffer_gles.h
@@ -26,7 +26,7 @@
ReactorGLES::Ref reactor);
// |CommandBuffer|
- void SetLabel(const std::string& label) const override;
+ void SetLabel(std::string_view label) const override;
// |CommandBuffer|
bool IsValid() const override;
diff --git a/impeller/renderer/backend/gles/device_buffer_gles.cc b/impeller/renderer/backend/gles/device_buffer_gles.cc
index 38c1e59..f4caaf5 100644
--- a/impeller/renderer/backend/gles/device_buffer_gles.cc
+++ b/impeller/renderer/backend/gles/device_buffer_gles.cc
@@ -110,13 +110,15 @@
}
// |DeviceBuffer|
-bool DeviceBufferGLES::SetLabel(const std::string& label) {
+bool DeviceBufferGLES::SetLabel(std::string_view label) {
+#ifdef IMPELLER_DEBUG
reactor_->SetDebugLabel(handle_, label);
+#endif // IMPELLER_DEBUG
return true;
}
// |DeviceBuffer|
-bool DeviceBufferGLES::SetLabel(const std::string& label, Range range) {
+bool DeviceBufferGLES::SetLabel(std::string_view label, Range range) {
// Cannot support debug label on the range. Set the label for the entire
// range.
return SetLabel(label);
diff --git a/impeller/renderer/backend/gles/device_buffer_gles.h b/impeller/renderer/backend/gles/device_buffer_gles.h
index db65d0c..a18e010 100644
--- a/impeller/renderer/backend/gles/device_buffer_gles.h
+++ b/impeller/renderer/backend/gles/device_buffer_gles.h
@@ -56,10 +56,10 @@
size_t offset) override;
// |DeviceBuffer|
- bool SetLabel(const std::string& label) override;
+ bool SetLabel(std::string_view label) override;
// |DeviceBuffer|
- bool SetLabel(const std::string& label, Range range) override;
+ bool SetLabel(std::string_view label, Range range) override;
DeviceBufferGLES(const DeviceBufferGLES&) = delete;
diff --git a/impeller/renderer/backend/gles/pipeline_library_gles.cc b/impeller/renderer/backend/gles/pipeline_library_gles.cc
index 6583142..493aabd 100644
--- a/impeller/renderer/backend/gles/pipeline_library_gles.cc
+++ b/impeller/renderer/backend/gles/pipeline_library_gles.cc
@@ -48,7 +48,7 @@
static void LogShaderCompilationFailure(const ProcTableGLES& gl,
GLuint shader,
- const std::string& name,
+ std::string_view name,
const fml::Mapping& source_mapping,
ShaderStage stage) {
std::stringstream stream;
@@ -99,10 +99,9 @@
}
gl.SetDebugLabel(DebugResourceType::kShader, vert_shader,
- SPrintF("%s Vertex Shader", descriptor.GetLabel().c_str()));
- gl.SetDebugLabel(
- DebugResourceType::kShader, frag_shader,
- SPrintF("%s Fragment Shader", descriptor.GetLabel().c_str()));
+ SPrintF("%s Vertex Shader", descriptor.GetLabel().data()));
+ gl.SetDebugLabel(DebugResourceType::kShader, frag_shader,
+ SPrintF("%s Fragment Shader", descriptor.GetLabel().data()));
fml::ScopedCleanupClosure delete_vert_shader(
[&gl, vert_shader]() { gl.DeleteShader(vert_shader); });
diff --git a/impeller/renderer/backend/gles/reactor_gles.cc b/impeller/renderer/backend/gles/reactor_gles.cc
index 54e58ff..d8b7cb6 100644
--- a/impeller/renderer/backend/gles/reactor_gles.cc
+++ b/impeller/renderer/backend/gles/reactor_gles.cc
@@ -275,7 +275,8 @@
}
}
-void ReactorGLES::SetDebugLabel(const HandleGLES& handle, std::string label) {
+void ReactorGLES::SetDebugLabel(const HandleGLES& handle,
+ std::string_view label) {
if (!can_set_debug_labels_) {
return;
}
@@ -284,7 +285,7 @@
}
WriterLock handles_lock(handles_mutex_);
if (auto found = handles_.find(handle); found != handles_.end()) {
- found->second.pending_debug_label = std::move(label);
+ found->second.pending_debug_label = label;
}
}
diff --git a/impeller/renderer/backend/gles/reactor_gles.h b/impeller/renderer/backend/gles/reactor_gles.h
index b427c3f..a2e3ce6 100644
--- a/impeller/renderer/backend/gles/reactor_gles.h
+++ b/impeller/renderer/backend/gles/reactor_gles.h
@@ -190,7 +190,7 @@
/// @param[in] handle The handle
/// @param[in] label The label
///
- void SetDebugLabel(const HandleGLES& handle, std::string label);
+ void SetDebugLabel(const HandleGLES& handle, std::string_view label);
using Operation = std::function<void(const ReactorGLES& reactor)>;
diff --git a/impeller/renderer/backend/gles/render_pass_gles.cc b/impeller/renderer/backend/gles/render_pass_gles.cc
index 54d8441..3906329 100644
--- a/impeller/renderer/backend/gles/render_pass_gles.cc
+++ b/impeller/renderer/backend/gles/render_pass_gles.cc
@@ -36,8 +36,8 @@
}
// |RenderPass|
-void RenderPassGLES::OnSetLabel(std::string label) {
- label_ = std::move(label);
+void RenderPassGLES::OnSetLabel(std::string_view label) {
+ label_ = label;
}
void ConfigureBlending(const ProcTableGLES& gl,
diff --git a/impeller/renderer/backend/gles/render_pass_gles.h b/impeller/renderer/backend/gles/render_pass_gles.h
index 9d4f986..e753a75 100644
--- a/impeller/renderer/backend/gles/render_pass_gles.h
+++ b/impeller/renderer/backend/gles/render_pass_gles.h
@@ -34,7 +34,7 @@
bool IsValid() const override;
// |RenderPass|
- void OnSetLabel(std::string label) override;
+ void OnSetLabel(std::string_view label) override;
// |RenderPass|
bool OnEncodeCommands(const Context& context) const override;
diff --git a/impeller/renderer/backend/metal/blit_pass_mtl.h b/impeller/renderer/backend/metal/blit_pass_mtl.h
index fed45cb..7883819 100644
--- a/impeller/renderer/backend/metal/blit_pass_mtl.h
+++ b/impeller/renderer/backend/metal/blit_pass_mtl.h
@@ -35,7 +35,7 @@
bool IsValid() const override;
// |BlitPass|
- void OnSetLabel(std::string label) override;
+ void OnSetLabel(std::string_view label) override;
// |BlitPass|
bool EncodeCommands(
@@ -50,26 +50,26 @@
std::shared_ptr<Texture> destination,
IRect source_region,
IPoint destination_origin,
- std::string label) override;
+ std::string_view label) override;
// |BlitPass|
bool OnCopyTextureToBufferCommand(std::shared_ptr<Texture> source,
std::shared_ptr<DeviceBuffer> destination,
IRect source_region,
size_t destination_offset,
- std::string label) override;
+ std::string_view label) override;
// |BlitPass|
bool OnCopyBufferToTextureCommand(BufferView source,
std::shared_ptr<Texture> destination,
IRect destination_region,
- std::string label,
+ std::string_view label,
uint32_t mip_level,
uint32_t slice,
bool convert_to_read) override;
// |BlitPass|
bool OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
- std::string label) override;
+ std::string_view label) override;
BlitPassMTL(const BlitPassMTL&) = delete;
diff --git a/impeller/renderer/backend/metal/blit_pass_mtl.mm b/impeller/renderer/backend/metal/blit_pass_mtl.mm
index d1b583b..09f381f 100644
--- a/impeller/renderer/backend/metal/blit_pass_mtl.mm
+++ b/impeller/renderer/backend/metal/blit_pass_mtl.mm
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "impeller/renderer/backend/metal/blit_pass_mtl.h"
+
#include <Metal/Metal.h>
#import <MetalPerformanceShaders/MetalPerformanceShaders.h>
#include <cstdint>
@@ -50,11 +51,11 @@
return is_valid_;
}
-void BlitPassMTL::OnSetLabel(std::string label) {
+void BlitPassMTL::OnSetLabel(std::string_view label) {
if (label.empty()) {
return;
}
- [encoder_ setLabel:@(label.c_str())];
+ [encoder_ setLabel:@(label.data())];
}
bool BlitPassMTL::EncodeCommands(
@@ -70,7 +71,7 @@
std::shared_ptr<Texture> destination,
IRect source_region,
IPoint destination_origin,
- std::string label) {
+ std::string_view label) {
auto source_mtl = TextureMTL::Cast(*source).GetMTLTexture();
if (!source_mtl) {
return false;
@@ -90,7 +91,7 @@
#ifdef IMPELLER_DEBUG
if (is_metal_trace_active_) {
- [encoder_ pushDebugGroup:@(label.c_str())];
+ [encoder_ pushDebugGroup:@(label.data())];
}
#endif // IMPELLER_DEBUG
[encoder_ copyFromTexture:source_mtl
@@ -139,7 +140,7 @@
std::shared_ptr<DeviceBuffer> destination,
IRect source_region,
size_t destination_offset,
- std::string label) {
+ std::string_view label) {
auto source_mtl = TextureMTL::Cast(*source).GetMTLTexture();
if (!source_mtl) {
return false;
@@ -164,7 +165,7 @@
#ifdef IMPELLER_DEBUG
if (is_metal_trace_active_) {
- [encoder_ pushDebugGroup:@(label.c_str())];
+ [encoder_ pushDebugGroup:@(label.data())];
}
#endif // IMPELLER_DEBUG
[encoder_ copyFromTexture:source_mtl
@@ -189,7 +190,7 @@
BufferView source,
std::shared_ptr<Texture> destination,
IRect destination_region,
- std::string label,
+ std::string_view label,
uint32_t mip_level,
uint32_t slice,
bool convert_to_read) {
@@ -215,7 +216,7 @@
#ifdef IMPELLER_DEBUG
if (is_metal_trace_active_) {
- [encoder_ pushDebugGroup:@(label.c_str())];
+ [encoder_ pushDebugGroup:@(label.data())];
}
#endif // IMPELLER_DEBUG
[encoder_
@@ -241,10 +242,10 @@
// |BlitPass|
bool BlitPassMTL::OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
- std::string label) {
+ std::string_view label) {
#ifdef IMPELLER_DEBUG
if (is_metal_trace_active_) {
- [encoder_ pushDebugGroup:@(label.c_str())];
+ [encoder_ pushDebugGroup:@(label.data())];
}
#endif // IMPELLER_DEBUG
auto result = TextureMTL::Cast(*texture).GenerateMipmap(encoder_);
diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.h b/impeller/renderer/backend/metal/command_buffer_mtl.h
index dad6379..6c5c948 100644
--- a/impeller/renderer/backend/metal/command_buffer_mtl.h
+++ b/impeller/renderer/backend/metal/command_buffer_mtl.h
@@ -28,7 +28,7 @@
id<MTLCommandQueue> queue);
// |CommandBuffer|
- void SetLabel(const std::string& label) const override;
+ void SetLabel(std::string_view label) const override;
// |CommandBuffer|
bool IsValid() const override;
diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm
index 21e9873..93bee9c 100644
--- a/impeller/renderer/backend/metal/command_buffer_mtl.mm
+++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm
@@ -140,12 +140,14 @@
return buffer_ != nil;
}
-void CommandBufferMTL::SetLabel(const std::string& label) const {
+void CommandBufferMTL::SetLabel(std::string_view label) const {
+#ifdef IMPELLER_DEBUG
if (label.empty()) {
return;
}
[buffer_ setLabel:@(label.data())];
+#endif // IMPELLER_DEBUG
}
static CommandBuffer::Status ToCommitResult(MTLCommandBufferStatus status) {
diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.h b/impeller/renderer/backend/metal/device_buffer_mtl.h
index 6980669..dbc61e2 100644
--- a/impeller/renderer/backend/metal/device_buffer_mtl.h
+++ b/impeller/renderer/backend/metal/device_buffer_mtl.h
@@ -42,10 +42,10 @@
size_t offset) override;
// |DeviceBuffer|
- bool SetLabel(const std::string& label) override;
+ bool SetLabel(std::string_view label) override;
// |DeviceBuffer|
- bool SetLabel(const std::string& label, Range range) override;
+ bool SetLabel(std::string_view label, Range range) override;
// |DeviceBuffer|
void Flush(std::optional<Range> range) const override;
diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.mm b/impeller/renderer/backend/metal/device_buffer_mtl.mm
index ff763f2..4d0e053 100644
--- a/impeller/renderer/backend/metal/device_buffer_mtl.mm
+++ b/impeller/renderer/backend/metal/device_buffer_mtl.mm
@@ -64,20 +64,24 @@
#endif
}
-bool DeviceBufferMTL::SetLabel(const std::string& label) {
+bool DeviceBufferMTL::SetLabel(std::string_view label) {
+#ifdef IMPELLER_DEBUG
if (label.empty()) {
return false;
}
- [buffer_ setLabel:@(label.c_str())];
+ [buffer_ setLabel:@(label.data())];
+#endif // IMPELLER_DEBUG
return true;
}
-bool DeviceBufferMTL::SetLabel(const std::string& label, Range range) {
+bool DeviceBufferMTL::SetLabel(std::string_view label, Range range) {
+#ifdef IMPELLER_DEBUG
if (label.empty()) {
return false;
}
- [buffer_ addDebugMarker:@(label.c_str())
+ [buffer_ addDebugMarker:@(label.data())
range:NSMakeRange(range.offset, range.length)];
+#endif // IMPELLER_DEBUG
return true;
}
diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.mm b/impeller/renderer/backend/metal/pipeline_library_mtl.mm
index 5f0dbd4..6c157d1 100644
--- a/impeller/renderer/backend/metal/pipeline_library_mtl.mm
+++ b/impeller/renderer/backend/metal/pipeline_library_mtl.mm
@@ -28,7 +28,7 @@
static void GetMTLRenderPipelineDescriptor(const PipelineDescriptor& desc,
const Callback& callback) {
auto descriptor = [[MTLRenderPipelineDescriptor alloc] init];
- descriptor.label = @(desc.GetLabel().c_str());
+ descriptor.label = @(desc.GetLabel().data());
descriptor.rasterSampleCount = static_cast<NSUInteger>(desc.GetSampleCount());
bool created_specialized_function = false;
diff --git a/impeller/renderer/backend/metal/render_pass_mtl.h b/impeller/renderer/backend/metal/render_pass_mtl.h
index 60cfe52..86f5a3c 100644
--- a/impeller/renderer/backend/metal/render_pass_mtl.h
+++ b/impeller/renderer/backend/metal/render_pass_mtl.h
@@ -55,7 +55,7 @@
bool IsValid() const override;
// |RenderPass|
- void OnSetLabel(std::string label) override;
+ void OnSetLabel(std::string_view label) override;
// |RenderPass|
bool OnEncodeCommands(const Context& context) const override;
diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm
index 3c9fda4..b03ae56 100644
--- a/impeller/renderer/backend/metal/render_pass_mtl.mm
+++ b/impeller/renderer/backend/metal/render_pass_mtl.mm
@@ -170,7 +170,7 @@
return is_valid_;
}
-void RenderPassMTL::OnSetLabel(std::string label) {
+void RenderPassMTL::OnSetLabel(std::string_view label) {
#ifdef IMPELLER_DEBUG
if (label.empty()) {
return;
diff --git a/impeller/renderer/backend/vulkan/blit_pass_vk.cc b/impeller/renderer/backend/vulkan/blit_pass_vk.cc
index 5c4aaba..5a3b2ef 100644
--- a/impeller/renderer/backend/vulkan/blit_pass_vk.cc
+++ b/impeller/renderer/backend/vulkan/blit_pass_vk.cc
@@ -49,12 +49,7 @@
BlitPassVK::~BlitPassVK() = default;
-void BlitPassVK::OnSetLabel(std::string label) {
- if (label.empty()) {
- return;
- }
- label_ = std::move(label);
-}
+void BlitPassVK::OnSetLabel(std::string_view label) {}
// |BlitPass|
bool BlitPassVK::IsValid() const {
@@ -73,7 +68,7 @@
std::shared_ptr<Texture> destination,
IRect source_region,
IPoint destination_origin,
- std::string label) {
+ std::string_view label) {
const auto& cmd_buffer = command_buffer_->GetCommandBuffer();
const auto& src = TextureVK::Cast(*source);
@@ -156,7 +151,7 @@
std::shared_ptr<DeviceBuffer> destination,
IRect source_region,
size_t destination_offset,
- std::string label) {
+ std::string_view label) {
const auto& cmd_buffer = command_buffer_->GetCommandBuffer();
// cast source and destination to TextureVK
@@ -245,7 +240,7 @@
BufferView source,
std::shared_ptr<Texture> destination,
IRect destination_region,
- std::string label,
+ std::string_view label,
uint32_t mip_level,
uint32_t slice,
bool convert_to_read) {
@@ -401,7 +396,7 @@
// |BlitPass|
bool BlitPassVK::OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
- std::string label) {
+ std::string_view label) {
auto& src = TextureVK::Cast(*texture);
const auto size = src.GetTextureDescriptor().size;
diff --git a/impeller/renderer/backend/vulkan/blit_pass_vk.h b/impeller/renderer/backend/vulkan/blit_pass_vk.h
index 625693b..073d422 100644
--- a/impeller/renderer/backend/vulkan/blit_pass_vk.h
+++ b/impeller/renderer/backend/vulkan/blit_pass_vk.h
@@ -23,7 +23,6 @@
friend class CommandBufferVK;
std::shared_ptr<CommandBufferVK> command_buffer_;
- std::string label_;
explicit BlitPassVK(std::shared_ptr<CommandBufferVK> command_buffer);
@@ -31,7 +30,7 @@
bool IsValid() const override;
// |BlitPass|
- void OnSetLabel(std::string label) override;
+ void OnSetLabel(std::string_view label) override;
// |BlitPass|
bool EncodeCommands(
@@ -50,26 +49,26 @@
std::shared_ptr<Texture> destination,
IRect source_region,
IPoint destination_origin,
- std::string label) override;
+ std::string_view label) override;
// |BlitPass|
bool OnCopyTextureToBufferCommand(std::shared_ptr<Texture> source,
std::shared_ptr<DeviceBuffer> destination,
IRect source_region,
size_t destination_offset,
- std::string label) override;
+ std::string_view label) override;
// |BlitPass|
bool OnCopyBufferToTextureCommand(BufferView source,
std::shared_ptr<Texture> destination,
IRect destination_region,
- std::string label,
+ std::string_view label,
uint32_t mip_level,
uint32_t slice,
bool convert_to_read) override;
// |BlitPass|
bool OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
- std::string label) override;
+ std::string_view label) override;
BlitPassVK(const BlitPassVK&) = delete;
diff --git a/impeller/renderer/backend/vulkan/command_buffer_vk.cc b/impeller/renderer/backend/vulkan/command_buffer_vk.cc
index 1e58aff..abe4971 100644
--- a/impeller/renderer/backend/vulkan/command_buffer_vk.cc
+++ b/impeller/renderer/backend/vulkan/command_buffer_vk.cc
@@ -31,12 +31,14 @@
CommandBufferVK::~CommandBufferVK() = default;
-void CommandBufferVK::SetLabel(const std::string& label) const {
+void CommandBufferVK::SetLabel(std::string_view label) const {
+#ifdef IMPELLER_DEBUG
auto context = context_.lock();
if (!context) {
return;
}
ContextVK::Cast(*context).SetDebugName(GetCommandBuffer(), label);
+#endif // IMPELLER_DEBUG
}
bool CommandBufferVK::IsValid() const {
diff --git a/impeller/renderer/backend/vulkan/command_buffer_vk.h b/impeller/renderer/backend/vulkan/command_buffer_vk.h
index b03635a..6a78787 100644
--- a/impeller/renderer/backend/vulkan/command_buffer_vk.h
+++ b/impeller/renderer/backend/vulkan/command_buffer_vk.h
@@ -95,7 +95,7 @@
std::shared_ptr<FenceWaiterVK> fence_waiter);
// |CommandBuffer|
- void SetLabel(const std::string& label) const override;
+ void SetLabel(std::string_view label) const override;
// |CommandBuffer|
bool IsValid() const override;
diff --git a/impeller/renderer/backend/vulkan/device_buffer_vk.cc b/impeller/renderer/backend/vulkan/device_buffer_vk.cc
index a43a582..5e135a3 100644
--- a/impeller/renderer/backend/vulkan/device_buffer_vk.cc
+++ b/impeller/renderer/backend/vulkan/device_buffer_vk.cc
@@ -48,7 +48,8 @@
return true;
}
-bool DeviceBufferVK::SetLabel(const std::string& label) {
+bool DeviceBufferVK::SetLabel(std::string_view label) {
+#ifdef IMPELLER_DEBUG
auto context = context_.lock();
if (!context || !resource_->buffer.is_valid()) {
// The context could have died at this point.
@@ -57,11 +58,14 @@
::vmaSetAllocationName(resource_->buffer.get().allocator, //
resource_->buffer.get().allocation, //
- label.c_str() //
+ label.data() //
);
return ContextVK::Cast(*context).SetDebugName(resource_->buffer.get().buffer,
label);
+#else
+ return true;
+#endif // IMPELLER_DEBUG
}
void DeviceBufferVK::Flush(std::optional<Range> range) const {
@@ -78,7 +82,7 @@
flush_range.offset, flush_range.length);
}
-bool DeviceBufferVK::SetLabel(const std::string& label, Range range) {
+bool DeviceBufferVK::SetLabel(std::string_view label, Range range) {
// We do not have the ability to name ranges. Just name the whole thing.
return SetLabel(label);
}
diff --git a/impeller/renderer/backend/vulkan/device_buffer_vk.h b/impeller/renderer/backend/vulkan/device_buffer_vk.h
index 744453d..540c165 100644
--- a/impeller/renderer/backend/vulkan/device_buffer_vk.h
+++ b/impeller/renderer/backend/vulkan/device_buffer_vk.h
@@ -61,10 +61,10 @@
size_t offset) override;
// |DeviceBuffer|
- bool SetLabel(const std::string& label) override;
+ bool SetLabel(std::string_view label) override;
// |DeviceBuffer|
- bool SetLabel(const std::string& label, Range range) override;
+ bool SetLabel(std::string_view label, Range range) override;
// |DeviceBuffer|
void Flush(std::optional<Range> range) const override;
diff --git a/impeller/renderer/backend/vulkan/pipeline_vk.cc b/impeller/renderer/backend/vulkan/pipeline_vk.cc
index 727a60b..fba6034 100644
--- a/impeller/renderer/backend/vulkan/pipeline_vk.cc
+++ b/impeller/renderer/backend/vulkan/pipeline_vk.cc
@@ -7,6 +7,7 @@
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/status_or.h"
#include "flutter/fml/trace_event.h"
+#include "impeller/base/strings.h"
#include "impeller/base/timing.h"
#include "impeller/renderer/backend/vulkan/capabilities_vk.h"
#include "impeller/renderer/backend/vulkan/context_vk.h"
@@ -159,8 +160,11 @@
return {};
}
- ContextVK::SetDebugName(device, pass.get(),
- "Compat Render Pass: " + desc.GetLabel());
+#ifdef IMPELLER_DEBUG
+ ContextVK::SetDebugName(
+ device, pass.get(),
+ SPrintF("Compat Render Pass: %s", desc.GetLabel().data()));
+#endif // IMPELLER_DEBUG
return pass;
}
@@ -207,8 +211,11 @@
"unable to create uniform descriptors")};
}
- ContextVK::SetDebugName(device_holder->GetDevice(), descs_layout.get(),
- "Descriptor Set Layout " + desc.GetLabel());
+#ifdef IMPELLER_DEBUG
+ ContextVK::SetDebugName(
+ device_holder->GetDevice(), descs_layout.get(),
+ SPrintF("Descriptor Set Layout: %s", desc.GetLabel().data()));
+#endif // IMPELLER_DEBUG
return fml::StatusOr<vk::UniqueDescriptorSetLayout>(std::move(descs_layout));
}
@@ -229,8 +236,11 @@
"Could not create pipeline layout for pipeline.")};
}
- ContextVK::SetDebugName(device_holder->GetDevice(), *pipeline_layout.value,
- "Pipeline Layout " + desc.GetLabel());
+#ifdef IMPELLER_DEBUG
+ ContextVK::SetDebugName(
+ device_holder->GetDevice(), *pipeline_layout.value,
+ SPrintF("Pipeline Layout %s", desc.GetLabel().data()));
+#endif // IMPELLER_DEBUG
return std::move(pipeline_layout.value);
}
@@ -439,8 +449,10 @@
ReportPipelineCreationFeedback(desc, feedback);
}
+#ifdef IMPELLER_DEBUG
ContextVK::SetDebugName(device_holder->GetDevice(), *pipeline,
- "Pipeline " + desc.GetLabel());
+ SPrintF("Pipeline %s", desc.GetLabel().data()));
+#endif // IMPELLER_DEBUG
return std::move(pipeline);
}
@@ -451,8 +463,7 @@
const std::shared_ptr<DeviceHolderVK>& device_holder,
const std::weak_ptr<PipelineLibrary>& weak_library,
std::shared_ptr<SamplerVK> immutable_sampler) {
- TRACE_EVENT1("flutter", "PipelineVK::Create", "Name",
- desc.GetLabel().c_str());
+ TRACE_EVENT1("flutter", "PipelineVK::Create", "Name", desc.GetLabel().data());
auto library = weak_library.lock();
diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.cc b/impeller/renderer/backend/vulkan/render_pass_vk.cc
index df6d27d..ddf15f5 100644
--- a/impeller/renderer/backend/vulkan/render_pass_vk.cc
+++ b/impeller/renderer/backend/vulkan/render_pass_vk.cc
@@ -238,10 +238,9 @@
return is_valid_;
}
-void RenderPassVK::OnSetLabel(std::string label) {
+void RenderPassVK::OnSetLabel(std::string_view label) {
#ifdef IMPELLER_DEBUG
- ContextVK::Cast(*context_).SetDebugName(render_pass_->Get(),
- std::string(label).c_str());
+ ContextVK::Cast(*context_).SetDebugName(render_pass_->Get(), label.data());
#endif // IMPELLER_DEBUG
}
diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.h b/impeller/renderer/backend/vulkan/render_pass_vk.h
index fa59713..034f888 100644
--- a/impeller/renderer/backend/vulkan/render_pass_vk.h
+++ b/impeller/renderer/backend/vulkan/render_pass_vk.h
@@ -123,7 +123,7 @@
bool IsValid() const override;
// |RenderPass|
- void OnSetLabel(std::string label) override;
+ void OnSetLabel(std::string_view label) override;
// |RenderPass|
bool OnEncodeCommands(const Context& context) const override;
diff --git a/impeller/renderer/blit_pass.cc b/impeller/renderer/blit_pass.cc
index 2c8d3c4..7d199d3 100644
--- a/impeller/renderer/blit_pass.cc
+++ b/impeller/renderer/blit_pass.cc
@@ -16,18 +16,18 @@
BlitPass::~BlitPass() = default;
-void BlitPass::SetLabel(std::string label) {
+void BlitPass::SetLabel(std::string_view label) {
if (label.empty()) {
return;
}
- OnSetLabel(std::move(label));
+ OnSetLabel(label);
}
bool BlitPass::AddCopy(std::shared_ptr<Texture> source,
std::shared_ptr<Texture> destination,
std::optional<IRect> source_region,
IPoint destination_origin,
- std::string label) {
+ std::string_view label) {
if (!source) {
VALIDATION_LOG << "Attempted to add a texture blit with no source.";
return false;
@@ -77,14 +77,14 @@
return OnCopyTextureToTextureCommand(
std::move(source), std::move(destination), source_region.value(),
- destination_origin, std::move(label));
+ destination_origin, label);
}
bool BlitPass::AddCopy(std::shared_ptr<Texture> source,
std::shared_ptr<DeviceBuffer> destination,
std::optional<IRect> source_region,
size_t destination_offset,
- std::string label) {
+ std::string_view label) {
if (!source) {
VALIDATION_LOG << "Attempted to add a texture blit with no source.";
return false;
@@ -117,13 +117,13 @@
return OnCopyTextureToBufferCommand(std::move(source), std::move(destination),
source_region.value(), destination_offset,
- std::move(label));
+ label);
}
bool BlitPass::AddCopy(BufferView source,
std::shared_ptr<Texture> destination,
std::optional<IRect> destination_region,
- std::string label,
+ std::string_view label,
uint32_t mip_level,
uint32_t slice,
bool convert_to_read) {
@@ -162,9 +162,9 @@
return false;
}
- return OnCopyBufferToTextureCommand(
- std::move(source), std::move(destination), destination_region_value,
- std::move(label), mip_level, slice, convert_to_read);
+ return OnCopyBufferToTextureCommand(std::move(source), std::move(destination),
+ destination_region_value, label,
+ mip_level, slice, convert_to_read);
}
bool BlitPass::ConvertTextureToShaderRead(
@@ -173,14 +173,14 @@
}
bool BlitPass::GenerateMipmap(std::shared_ptr<Texture> texture,
- std::string label) {
+ std::string_view label) {
if (!texture) {
VALIDATION_LOG << "Attempted to add an invalid mipmap generation command "
"with no texture.";
return false;
}
- return OnGenerateMipmapCommand(std::move(texture), std::move(label));
+ return OnGenerateMipmapCommand(std::move(texture), label);
}
} // namespace impeller
diff --git a/impeller/renderer/blit_pass.h b/impeller/renderer/blit_pass.h
index 5c3b8af..761a402 100644
--- a/impeller/renderer/blit_pass.h
+++ b/impeller/renderer/blit_pass.h
@@ -30,7 +30,7 @@
virtual bool IsValid() const = 0;
- void SetLabel(std::string label);
+ void SetLabel(std::string_view label);
//----------------------------------------------------------------------------
/// @brief If the texture is not already in a shader read internal
@@ -71,7 +71,7 @@
std::shared_ptr<Texture> destination,
std::optional<IRect> source_region = std::nullopt,
IPoint destination_origin = {},
- std::string label = "");
+ std::string_view label = "");
//----------------------------------------------------------------------------
/// @brief Record a command to copy the contents of a texture to a
@@ -94,7 +94,7 @@
std::shared_ptr<DeviceBuffer> destination,
std::optional<IRect> source_region = std::nullopt,
size_t destination_offset = 0,
- std::string label = "");
+ std::string_view label = "");
//----------------------------------------------------------------------------
/// @brief Record a command to copy the contents of a buffer to a
@@ -127,7 +127,7 @@
bool AddCopy(BufferView source,
std::shared_ptr<Texture> destination,
std::optional<IRect> destination_region = std::nullopt,
- std::string label = "",
+ std::string_view label = "",
uint32_t mip_level = 0,
uint32_t slice = 0,
bool convert_to_read = true);
@@ -140,7 +140,8 @@
///
/// @return If the command was valid for subsequent commitment.
///
- bool GenerateMipmap(std::shared_ptr<Texture> texture, std::string label = "");
+ bool GenerateMipmap(std::shared_ptr<Texture> texture,
+ std::string_view label = "");
//----------------------------------------------------------------------------
/// @brief Encode the recorded commands to the underlying command buffer.
@@ -156,33 +157,33 @@
protected:
explicit BlitPass();
- virtual void OnSetLabel(std::string label) = 0;
+ virtual void OnSetLabel(std::string_view label) = 0;
virtual bool OnCopyTextureToTextureCommand(
std::shared_ptr<Texture> source,
std::shared_ptr<Texture> destination,
IRect source_region,
IPoint destination_origin,
- std::string label) = 0;
+ std::string_view label) = 0;
virtual bool OnCopyTextureToBufferCommand(
std::shared_ptr<Texture> source,
std::shared_ptr<DeviceBuffer> destination,
IRect source_region,
size_t destination_offset,
- std::string label) = 0;
+ std::string_view label) = 0;
virtual bool OnCopyBufferToTextureCommand(
BufferView source,
std::shared_ptr<Texture> destination,
IRect destination_region,
- std::string label,
+ std::string_view label,
uint32_t mip_level,
uint32_t slice,
bool convert_to_read) = 0;
virtual bool OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
- std::string label) = 0;
+ std::string_view label) = 0;
private:
BlitPass(const BlitPass&) = delete;
diff --git a/impeller/renderer/command_buffer.h b/impeller/renderer/command_buffer.h
index 4794941..958b4d0 100644
--- a/impeller/renderer/command_buffer.h
+++ b/impeller/renderer/command_buffer.h
@@ -58,7 +58,7 @@
virtual bool IsValid() const = 0;
- virtual void SetLabel(const std::string& label) const = 0;
+ virtual void SetLabel(std::string_view label) const = 0;
//----------------------------------------------------------------------------
/// @brief Force execution of pending GPU commands.
diff --git a/impeller/renderer/compute_pipeline_descriptor.cc b/impeller/renderer/compute_pipeline_descriptor.cc
index efb9cda..801eaee 100644
--- a/impeller/renderer/compute_pipeline_descriptor.cc
+++ b/impeller/renderer/compute_pipeline_descriptor.cc
@@ -33,8 +33,8 @@
}
ComputePipelineDescriptor& ComputePipelineDescriptor::SetLabel(
- std::string label) {
- label_ = std::move(label);
+ std::string_view label) {
+ label_ = label;
return *this;
}
diff --git a/impeller/renderer/compute_pipeline_descriptor.h b/impeller/renderer/compute_pipeline_descriptor.h
index 85311fe..112894b 100644
--- a/impeller/renderer/compute_pipeline_descriptor.h
+++ b/impeller/renderer/compute_pipeline_descriptor.h
@@ -24,7 +24,7 @@
~ComputePipelineDescriptor();
- ComputePipelineDescriptor& SetLabel(std::string label);
+ ComputePipelineDescriptor& SetLabel(std::string_view label);
const std::string& GetLabel() const;
diff --git a/impeller/renderer/pipeline_descriptor.cc b/impeller/renderer/pipeline_descriptor.cc
index 9ae6710..0e6243e 100644
--- a/impeller/renderer/pipeline_descriptor.cc
+++ b/impeller/renderer/pipeline_descriptor.cc
@@ -68,8 +68,8 @@
specialization_constants_ == other.specialization_constants_;
}
-PipelineDescriptor& PipelineDescriptor::SetLabel(std::string label) {
- label_ = std::move(label);
+PipelineDescriptor& PipelineDescriptor::SetLabel(std::string_view label) {
+ label_ = std::string(label);
return *this;
}
@@ -231,7 +231,7 @@
return nullptr;
}
-const std::string& PipelineDescriptor::GetLabel() const {
+std::string_view PipelineDescriptor::GetLabel() const {
return label_;
}
diff --git a/impeller/renderer/pipeline_descriptor.h b/impeller/renderer/pipeline_descriptor.h
index 3ea2ae6..83eb56f 100644
--- a/impeller/renderer/pipeline_descriptor.h
+++ b/impeller/renderer/pipeline_descriptor.h
@@ -27,9 +27,9 @@
~PipelineDescriptor();
- PipelineDescriptor& SetLabel(std::string label);
+ PipelineDescriptor& SetLabel(std::string_view label);
- const std::string& GetLabel() const;
+ std::string_view GetLabel() const;
PipelineDescriptor& SetSampleCount(SampleCount samples);
diff --git a/impeller/renderer/render_pass.cc b/impeller/renderer/render_pass.cc
index df68c49..a0c6e56 100644
--- a/impeller/renderer/render_pass.cc
+++ b/impeller/renderer/render_pass.cc
@@ -50,11 +50,11 @@
return orthographic_;
}
-void RenderPass::SetLabel(std::string label) {
+void RenderPass::SetLabel(std::string_view label) {
if (label.empty()) {
return;
}
- OnSetLabel(std::move(label));
+ OnSetLabel(label);
}
bool RenderPass::AddCommand(Command&& command) {
diff --git a/impeller/renderer/render_pass.h b/impeller/renderer/render_pass.h
index c0e9b73..9d83ed1 100644
--- a/impeller/renderer/render_pass.h
+++ b/impeller/renderer/render_pass.h
@@ -6,7 +6,6 @@
#define FLUTTER_IMPELLER_RENDERER_RENDER_PASS_H_
#include <cstddef>
-#include <string>
#include "fml/status.h"
#include "impeller/core/formats.h"
@@ -45,7 +44,7 @@
virtual bool IsValid() const = 0;
- void SetLabel(std::string label);
+ void SetLabel(std::string_view label);
/// @brief Reserve [command_count] commands in the HAL command buffer.
///
@@ -269,7 +268,7 @@
static bool ValidateIndexBuffer(const BufferView& index_buffer,
IndexType index_type);
- virtual void OnSetLabel(std::string label) = 0;
+ virtual void OnSetLabel(std::string_view label) = 0;
virtual bool OnEncodeCommands(const Context& context) const = 0;
diff --git a/impeller/renderer/render_target.cc b/impeller/renderer/render_target.cc
index 6afa486..389ecd2 100644
--- a/impeller/renderer/render_target.cc
+++ b/impeller/renderer/render_target.cc
@@ -260,7 +260,7 @@
const Context& context,
ISize size,
int mip_count,
- const std::string& label,
+ std::string_view label,
RenderTarget::AttachmentConfig color_attachment_config,
std::optional<RenderTarget::AttachmentConfig> stencil_attachment_config,
const std::shared_ptr<Texture>& existing_color_texture,
@@ -289,7 +289,9 @@
return {};
}
}
- color0_tex->SetLabel(SPrintF("%s Color Texture", label.c_str()));
+#ifdef IMPELLER_DEBUG
+ color0_tex->SetLabel(SPrintF("%s Color Texture", label.data()));
+#endif // IMPELLER_DEBUG
ColorAttachment color0;
color0.clear_color = color_attachment_config.clear_color;
@@ -314,7 +316,7 @@
const Context& context,
ISize size,
int mip_count,
- const std::string& label,
+ std::string_view label,
RenderTarget::AttachmentConfigMSAA color_attachment_config,
std::optional<RenderTarget::AttachmentConfig> stencil_attachment_config,
const std::shared_ptr<Texture>& existing_color_msaa_texture,
@@ -349,8 +351,10 @@
return {};
}
}
+#ifdef IMPELLER_DEBUG
color0_msaa_tex->SetLabel(
- SPrintF("%s Color Texture (Multisample)", label.c_str()));
+ SPrintF("%s Color Texture (Multisample)", label.data()));
+#endif // IMPELLER_DEBUG
// Create color resolve texture.
std::shared_ptr<Texture> color0_resolve_tex;
@@ -372,7 +376,9 @@
return {};
}
}
- color0_resolve_tex->SetLabel(SPrintF("%s Color Texture", label.c_str()));
+#ifdef IMPELLER_DEBUG
+ color0_resolve_tex->SetLabel(SPrintF("%s Color Texture", label.data()));
+#endif // IMPELLER_DEBUG
// Color attachment.
@@ -415,7 +421,7 @@
Allocator& allocator,
ISize size,
bool msaa,
- const std::string& label,
+ std::string_view label,
RenderTarget::AttachmentConfig stencil_attachment_config,
const std::shared_ptr<Texture>& existing_depth_stencil_texture) {
std::shared_ptr<Texture> depth_stencil_texture;
@@ -451,8 +457,9 @@
stencil0.clear_stencil = 0u;
stencil0.texture = std::move(depth_stencil_texture);
- stencil0.texture->SetLabel(
- SPrintF("%s Depth+Stencil Texture", label.c_str()));
+#ifdef IMPELLER_DEBUG
+ stencil0.texture->SetLabel(SPrintF("%s Depth+Stencil Texture", label.data()));
+#endif // IMPELLER_DEBUG
SetDepthAttachment(std::move(depth0));
SetStencilAttachment(std::move(stencil0));
}
diff --git a/impeller/renderer/render_target.h b/impeller/renderer/render_target.h
index 6d63527..77f5bf4 100644
--- a/impeller/renderer/render_target.h
+++ b/impeller/renderer/render_target.h
@@ -82,7 +82,7 @@
Allocator& allocator,
ISize size,
bool msaa,
- const std::string& label = "Offscreen",
+ std::string_view label = "Offscreen",
RenderTarget::AttachmentConfig stencil_attachment_config =
RenderTarget::kDefaultStencilAttachmentConfig,
const std::shared_ptr<Texture>& depth_stencil_texture = nullptr);
@@ -149,7 +149,7 @@
const Context& context,
ISize size,
int mip_count,
- const std::string& label = "Offscreen",
+ std::string_view label = "Offscreen",
RenderTarget::AttachmentConfig color_attachment_config =
RenderTarget::kDefaultColorAttachmentConfig,
std::optional<RenderTarget::AttachmentConfig> stencil_attachment_config =
@@ -161,7 +161,7 @@
const Context& context,
ISize size,
int mip_count,
- const std::string& label = "Offscreen MSAA",
+ std::string_view label = "Offscreen MSAA",
RenderTarget::AttachmentConfigMSAA color_attachment_config =
RenderTarget::kDefaultColorAttachmentConfigMSAA,
std::optional<RenderTarget::AttachmentConfig> stencil_attachment_config =
diff --git a/impeller/renderer/testing/mocks.h b/impeller/renderer/testing/mocks.h
index cc781b8..1e02636 100644
--- a/impeller/renderer/testing/mocks.h
+++ b/impeller/renderer/testing/mocks.h
@@ -28,11 +28,11 @@
explicit MockDeviceBuffer(const DeviceBufferDescriptor& desc)
: DeviceBuffer(desc) {}
- MOCK_METHOD(bool, SetLabel, (const std::string& label), (override));
+ MOCK_METHOD(bool, SetLabel, (std::string_view label), (override));
MOCK_METHOD(bool,
SetLabel,
- (const std::string& label, Range range),
+ (std::string_view label, Range range),
(override));
MOCK_METHOD(uint8_t*, OnGetContents, (), (const, override));
@@ -63,7 +63,7 @@
EncodeCommands,
(const std::shared_ptr<Allocator>& transients_allocator),
(const, override));
- MOCK_METHOD(void, OnSetLabel, (std::string label), (override));
+ MOCK_METHOD(void, OnSetLabel, (std::string_view label), (override));
MOCK_METHOD(bool,
ResizeTexture,
@@ -77,7 +77,7 @@
std::shared_ptr<Texture> destination,
IRect source_region,
IPoint destination_origin,
- std::string label),
+ std::string_view label),
(override));
MOCK_METHOD(bool,
@@ -86,21 +86,21 @@
std::shared_ptr<DeviceBuffer> destination,
IRect source_region,
size_t destination_offset,
- std::string label),
+ std::string_view label),
(override));
MOCK_METHOD(bool,
OnCopyBufferToTextureCommand,
(BufferView source,
std::shared_ptr<Texture> destination,
IRect destination_rect,
- std::string label,
+ std::string_view label,
uint32_t mip_level,
uint32_t slice,
bool convert_to_read),
(override));
MOCK_METHOD(bool,
OnGenerateMipmapCommand,
- (std::shared_ptr<Texture> texture, std::string label),
+ (std::shared_ptr<Texture> texture, std::string_view label),
(override));
};
@@ -114,7 +114,7 @@
OnEncodeCommands,
(const Context& context),
(const, override));
- MOCK_METHOD(void, OnSetLabel, (std::string label), (override));
+ MOCK_METHOD(void, OnSetLabel, (std::string_view label), (override));
};
class MockCommandBuffer : public CommandBuffer {
@@ -122,7 +122,7 @@
explicit MockCommandBuffer(std::weak_ptr<const Context> context)
: CommandBuffer(std::move(context)) {}
MOCK_METHOD(bool, IsValid, (), (const, override));
- MOCK_METHOD(void, SetLabel, (const std::string& label), (const, override));
+ MOCK_METHOD(void, SetLabel, (std::string_view label), (const, override));
MOCK_METHOD(std::shared_ptr<BlitPass>, OnCreateBlitPass, (), (override));
MOCK_METHOD(bool,
OnSubmitCommands,
diff --git a/impeller/toolkit/interop/impeller.cc b/impeller/toolkit/interop/impeller.cc
index 1484eda..05d8c03 100644
--- a/impeller/toolkit/interop/impeller.cc
+++ b/impeller/toolkit/interop/impeller.cc
@@ -1009,7 +1009,8 @@
IMPELLER_EXTERN_C
ImpellerParagraphBuilder ImpellerParagraphBuilderNew(
ImpellerTypographyContext context) {
- auto builder = Create<ParagraphBuilder>(*GetPeer(context));
+ auto builder =
+ Create<ParagraphBuilder>(Ref<TypographyContext>(GetPeer(context)));
if (!builder->IsValid()) {
VALIDATION_LOG << "Could not create valid paragraph builder.";
return nullptr;
diff --git a/impeller/toolkit/interop/impeller_unittests.cc b/impeller/toolkit/interop/impeller_unittests.cc
index c582944..2a4690e 100644
--- a/impeller/toolkit/interop/impeller_unittests.cc
+++ b/impeller/toolkit/interop/impeller_unittests.cc
@@ -245,4 +245,97 @@
}));
}
+static void DrawTextFrame(ImpellerTypographyContext tc,
+ ImpellerDisplayListBuilder builder,
+ ImpellerParagraphStyle p_style,
+ ImpellerPaint bg,
+ ImpellerColor color,
+ ImpellerTextAlignment align,
+ float x_offset) {
+ const char text[] =
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
+
+ ImpellerPaint fg = ImpellerPaintNew();
+
+ // Draw a box.
+ ImpellerPaintSetColor(fg, &color);
+ ImpellerPaintSetDrawStyle(fg, kImpellerDrawStyleStroke);
+ ImpellerRect box_rect = {10 + x_offset, 10, 200, 200};
+ ImpellerDisplayListBuilderDrawRect(builder, &box_rect, fg);
+
+ // Draw text.
+ ImpellerPaintSetDrawStyle(fg, kImpellerDrawStyleFill);
+ ImpellerParagraphStyleSetForeground(p_style, fg);
+ ImpellerParagraphStyleSetBackground(p_style, bg);
+ ImpellerParagraphStyleSetTextAlignment(p_style, align);
+ ImpellerParagraphBuilder p_builder = ImpellerParagraphBuilderNew(tc);
+ ImpellerParagraphBuilderPushStyle(p_builder, p_style);
+ ImpellerParagraphBuilderAddText(p_builder, (const uint8_t*)text,
+ sizeof(text));
+ ImpellerParagraph left_p = ImpellerParagraphBuilderBuildParagraphNew(
+ p_builder, box_rect.width - 20.0);
+ ImpellerPoint pt = {20.0f + x_offset, 20.0f};
+ float w = ImpellerParagraphGetMaxWidth(left_p);
+ float h = ImpellerParagraphGetHeight(left_p);
+ ImpellerDisplayListBuilderDrawParagraph(builder, left_p, &pt);
+ ImpellerPaintSetDrawStyle(fg, kImpellerDrawStyleStroke);
+
+ // Draw an inner box around the paragraph layout.
+ ImpellerRect inner_box_rect = {pt.x, pt.y, w, h};
+ ImpellerDisplayListBuilderDrawRect(builder, &inner_box_rect, fg);
+
+ ImpellerParagraphRelease(left_p);
+ ImpellerParagraphBuilderRelease(p_builder);
+ ImpellerPaintRelease(fg);
+}
+
+TEST_P(InteropPlaygroundTest, CanRenderTextAlignments) {
+ ImpellerTypographyContext tc = ImpellerTypographyContextNew();
+
+ ImpellerDisplayList dl = NULL;
+
+ {
+ ImpellerDisplayListBuilder builder = ImpellerDisplayListBuilderNew(NULL);
+ ImpellerPaint bg = ImpellerPaintNew();
+ ImpellerParagraphStyle p_style = ImpellerParagraphStyleNew();
+ ImpellerParagraphStyleSetFontFamily(p_style, "Roboto");
+ ImpellerParagraphStyleSetFontSize(p_style, 24.0);
+ ImpellerParagraphStyleSetFontWeight(p_style, kImpellerFontWeight400);
+
+ // Clear the background to a white color.
+ ImpellerColor clear_color = {1.0, 1.0, 1.0, 1.0};
+ ImpellerPaintSetColor(bg, &clear_color);
+ ImpellerDisplayListBuilderDrawPaint(builder, bg);
+
+ // Draw red, left-aligned text.
+ ImpellerColor red = {1.0, 0.0, 0.0, 1.0};
+ DrawTextFrame(tc, builder, p_style, bg, red, kImpellerTextAlignmentLeft,
+ 0.0);
+
+ // Draw green, centered text.
+ ImpellerColor green = {0.0, 1.0, 0.0, 1.0};
+ DrawTextFrame(tc, builder, p_style, bg, green, kImpellerTextAlignmentCenter,
+ 220.0);
+
+ // Draw blue, right-aligned text.
+ ImpellerColor blue = {0.0, 0.0, 1.0, 1.0};
+ DrawTextFrame(tc, builder, p_style, bg, blue, kImpellerTextAlignmentRight,
+ 440.0);
+
+ dl = ImpellerDisplayListBuilderCreateDisplayListNew(builder);
+
+ ImpellerPaintRelease(bg);
+ ImpellerDisplayListBuilderRelease(builder);
+ }
+
+ ASSERT_TRUE(
+ OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
+ ImpellerSurfaceDrawDisplayList(surface.GetC(), dl);
+ return true;
+ }));
+
+ ImpellerDisplayListRelease(dl);
+ ImpellerTypographyContextRelease(tc);
+}
+
} // namespace impeller::interop::testing
diff --git a/impeller/toolkit/interop/paragraph_builder.cc b/impeller/toolkit/interop/paragraph_builder.cc
index 4997d59..d983c38 100644
--- a/impeller/toolkit/interop/paragraph_builder.cc
+++ b/impeller/toolkit/interop/paragraph_builder.cc
@@ -10,41 +10,29 @@
namespace impeller::interop {
-ParagraphBuilder::ParagraphBuilder(const TypographyContext& context) {
- if (!context.IsValid()) {
- VALIDATION_LOG << "Invalid typography context.";
- return;
- }
-
- static txt::ParagraphStyle kBaseStyle;
-
- builder_ = std::make_unique<txt::ParagraphBuilderSkia>(
- kBaseStyle, //
- context.GetFontCollection(), //
- true // is impeller enabled
- );
-}
+ParagraphBuilder::ParagraphBuilder(ScopedObject<TypographyContext> context)
+ : context_(std::move(context)) {}
ParagraphBuilder::~ParagraphBuilder() = default;
bool ParagraphBuilder::IsValid() const {
- return !!builder_;
+ return !!context_;
}
void ParagraphBuilder::PushStyle(const ParagraphStyle& style) {
- builder_->PushStyle(style.CreateTextStyle());
+ GetBuilder(style.GetParagraphStyle())->PushStyle(style.CreateTextStyle());
}
void ParagraphBuilder::PopStyle() {
- builder_->Pop();
+ GetBuilder()->Pop();
}
void ParagraphBuilder::AddText(const uint8_t* data, size_t byte_length) {
- builder_->AddText(data, byte_length);
+ GetBuilder()->AddText(data, byte_length);
}
ScopedObject<Paragraph> ParagraphBuilder::Build(Scalar width) const {
- auto txt_paragraph = builder_->Build();
+ auto txt_paragraph = GetBuilder()->Build();
if (!txt_paragraph) {
return nullptr;
}
@@ -52,4 +40,23 @@
return Create<Paragraph>(std::move(txt_paragraph));
}
+const std::unique_ptr<txt::ParagraphBuilder>& ParagraphBuilder::GetBuilder(
+ const txt::ParagraphStyle& style) const {
+ if (lazy_builder_) {
+ return lazy_builder_;
+ }
+ lazy_builder_ = std::make_unique<txt::ParagraphBuilderSkia>(
+ style, //
+ context_->GetFontCollection(), //
+ true // is impeller enabled
+ );
+ return lazy_builder_;
+}
+
+const std::unique_ptr<txt::ParagraphBuilder>& ParagraphBuilder::GetBuilder()
+ const {
+ static txt::ParagraphStyle kDefaultStyle;
+ return GetBuilder(kDefaultStyle);
+}
+
} // namespace impeller::interop
diff --git a/impeller/toolkit/interop/paragraph_builder.h b/impeller/toolkit/interop/paragraph_builder.h
index 1560446..8ea141e 100644
--- a/impeller/toolkit/interop/paragraph_builder.h
+++ b/impeller/toolkit/interop/paragraph_builder.h
@@ -20,7 +20,7 @@
: public Object<ParagraphBuilder,
IMPELLER_INTERNAL_HANDLE_NAME(ImpellerParagraphBuilder)> {
public:
- explicit ParagraphBuilder(const TypographyContext& context);
+ explicit ParagraphBuilder(ScopedObject<TypographyContext> context);
~ParagraphBuilder() override;
@@ -39,7 +39,13 @@
ScopedObject<Paragraph> Build(Scalar width) const;
private:
- std::unique_ptr<txt::ParagraphBuilder> builder_;
+ ScopedObject<TypographyContext> context_;
+ mutable std::unique_ptr<txt::ParagraphBuilder> lazy_builder_;
+
+ const std::unique_ptr<txt::ParagraphBuilder>& GetBuilder(
+ const txt::ParagraphStyle& style) const;
+
+ const std::unique_ptr<txt::ParagraphBuilder>& GetBuilder() const;
};
} // namespace impeller::interop
diff --git a/impeller/toolkit/interop/paragraph_style.cc b/impeller/toolkit/interop/paragraph_style.cc
index 14733dc..55a7fcd 100644
--- a/impeller/toolkit/interop/paragraph_style.cc
+++ b/impeller/toolkit/interop/paragraph_style.cc
@@ -51,18 +51,23 @@
}
void ParagraphStyle::SetBackground(ScopedObject<Paint> paint) {
- backgrond_ = std::move(paint);
+ background_ = std::move(paint);
}
txt::TextStyle ParagraphStyle::CreateTextStyle() const {
auto style = style_.GetTextStyle();
+
if (foreground_) {
style.foreground = foreground_->GetPaint();
}
- if (backgrond_) {
- style.background = backgrond_->GetPaint();
+ if (background_) {
+ style.background = background_->GetPaint();
}
return style;
}
+const txt::ParagraphStyle& ParagraphStyle::GetParagraphStyle() const {
+ return style_;
+}
+
} // namespace impeller::interop
diff --git a/impeller/toolkit/interop/paragraph_style.h b/impeller/toolkit/interop/paragraph_style.h
index 37165e3..0a3e898 100644
--- a/impeller/toolkit/interop/paragraph_style.h
+++ b/impeller/toolkit/interop/paragraph_style.h
@@ -48,10 +48,12 @@
txt::TextStyle CreateTextStyle() const;
+ const txt::ParagraphStyle& GetParagraphStyle() const;
+
private:
txt::ParagraphStyle style_;
ScopedObject<Paint> foreground_;
- ScopedObject<Paint> backgrond_;
+ ScopedObject<Paint> background_;
};
} // namespace impeller::interop
diff --git a/lib/ui/painting/image_decoder_impeller.cc b/lib/ui/painting/image_decoder_impeller.cc
index adb06b4..6a11189 100644
--- a/lib/ui/painting/image_decoder_impeller.cc
+++ b/lib/ui/painting/image_decoder_impeller.cc
@@ -483,6 +483,9 @@
}
texture->SetLabel(impeller::SPrintF("ui.Image(%p)", texture.get()).c_str());
+
+ context->DisposeThreadLocalCachedResources();
+
return std::make_pair(impeller::DlImageImpeller::Make(std::move(texture)),
std::string());
}
diff --git a/lib/ui/painting/image_decoder_no_gl_unittests.h b/lib/ui/painting/image_decoder_no_gl_unittests.h
index b1839f0..4f4cdf2 100644
--- a/lib/ui/painting/image_decoder_no_gl_unittests.h
+++ b/lib/ui/painting/image_decoder_no_gl_unittests.h
@@ -44,9 +44,9 @@
~TestImpellerDeviceBuffer() { free(bytes_); }
private:
- bool SetLabel(const std::string& label) override { return true; }
+ bool SetLabel(std::string_view label) override { return true; }
- bool SetLabel(const std::string& label, Range range) override { return true; }
+ bool SetLabel(std::string_view label, Range range) override { return true; }
uint8_t* OnGetContents() const override { return bytes_; }
diff --git a/lib/ui/painting/image_decoder_unittests.cc b/lib/ui/painting/image_decoder_unittests.cc
index d8f7667..69096ee 100644
--- a/lib/ui/painting/image_decoder_unittests.cc
+++ b/lib/ui/painting/image_decoder_unittests.cc
@@ -92,8 +92,12 @@
tasks_.clear();
}
+ void DisposeThreadLocalCachedResources() override { did_dispose_ = true; }
+
void Shutdown() override {}
+ bool DidDisposeResources() const { return did_dispose_; }
+
mutable size_t command_buffer_count_ = 0;
private:
@@ -103,6 +107,7 @@
};
std::vector<PendingTask> tasks_;
std::shared_ptr<const Capabilities> capabilities_;
+ bool did_dispose_ = false;
};
} // namespace impeller
@@ -367,12 +372,14 @@
EXPECT_EQ(no_gpu_access_context->command_buffer_count_, 0ul);
EXPECT_FALSE(invoked);
+ EXPECT_EQ(no_gpu_access_context->DidDisposeResources(), false);
auto result = ImageDecoderImpeller::UploadTextureToStorage(
no_gpu_access_context, bitmap);
ASSERT_EQ(no_gpu_access_context->command_buffer_count_, 0ul);
ASSERT_EQ(result.second, "");
+ EXPECT_EQ(no_gpu_access_context->DidDisposeResources(), true);
no_gpu_access_context->FlushTasks(/*fail=*/true);
}
diff --git a/lib/ui/window/pointer_data_packet_converter.cc b/lib/ui/window/pointer_data_packet_converter.cc
index 45b05a0..b764028 100644
--- a/lib/ui/window/pointer_data_packet_converter.cc
+++ b/lib/ui/window/pointer_data_packet_converter.cc
@@ -74,7 +74,45 @@
break;
}
case PointerData::Change::kAdd: {
- FML_DCHECK(states_.find(pointer_data.device) == states_.end());
+ auto iter = states_.find(pointer_data.device);
+ if (iter != states_.end()) {
+ // Synthesizes a remove event if the pointer was not previously
+ // removed.
+ PointerState state = iter->second;
+ PointerData synthesized_data = pointer_data;
+ synthesized_data.physical_x = state.physical_x;
+ synthesized_data.physical_y = state.physical_y;
+ synthesized_data.pan_x = state.pan_x;
+ synthesized_data.pan_y = state.pan_y;
+ synthesized_data.scale = state.scale;
+ synthesized_data.rotation = state.rotation;
+ synthesized_data.buttons = state.buttons;
+ synthesized_data.synthesized = 1;
+
+ // The framework expects an add event to always follow a remove event,
+ // and remove events with invalid views are ignored.
+ // To meet the framework's expectations, the view ID of the add event
+ // is used for the remove event if the old view has been destroyed.
+ if (delegate_.ViewExists(state.view_id)) {
+ synthesized_data.view_id = state.view_id;
+
+ if (state.is_down) {
+ // Synthesizes cancel event if the pointer is down.
+ PointerData synthesized_cancel_event = synthesized_data;
+ synthesized_cancel_event.change = PointerData::Change::kCancel;
+ UpdatePointerIdentifier(synthesized_cancel_event, state, false);
+
+ state.is_down = false;
+ converted_pointers.push_back(synthesized_cancel_event);
+ }
+ }
+
+ PointerData synthesized_remove_event = synthesized_data;
+ synthesized_remove_event.change = PointerData::Change::kRemove;
+
+ converted_pointers.push_back(synthesized_remove_event);
+ }
+
EnsurePointerState(pointer_data);
converted_pointers.push_back(pointer_data);
break;
@@ -84,6 +122,11 @@
auto iter = states_.find(pointer_data.device);
FML_DCHECK(iter != states_.end());
PointerState state = iter->second;
+ if (state.view_id != pointer_data.view_id) {
+ // Ignores remove event if the pointer was previously added to a
+ // different view.
+ break;
+ }
if (state.is_down) {
// Synthesizes cancel event if the pointer is down.
@@ -362,6 +405,7 @@
state.physical_y = pointer_data.physical_y;
state.pan_x = 0;
state.pan_y = 0;
+ state.view_id = pointer_data.view_id;
states_[pointer_data.device] = state;
return state;
}
diff --git a/lib/ui/window/pointer_data_packet_converter.h b/lib/ui/window/pointer_data_packet_converter.h
index 146ade2..7b1b823 100644
--- a/lib/ui/window/pointer_data_packet_converter.h
+++ b/lib/ui/window/pointer_data_packet_converter.h
@@ -38,6 +38,7 @@
double scale;
double rotation;
int64_t buttons;
+ int64_t view_id;
};
//------------------------------------------------------------------------------
diff --git a/lib/ui/window/pointer_data_packet_converter_unittests.cc b/lib/ui/window/pointer_data_packet_converter_unittests.cc
index 4f6a37f..0eb9667 100644
--- a/lib/ui/window/pointer_data_packet_converter_unittests.cc
+++ b/lib/ui/window/pointer_data_packet_converter_unittests.cc
@@ -510,6 +510,108 @@
ASSERT_EQ(result[3].buttons, 0);
}
+TEST(PointerDataPacketConverterTest, CanSynthesizeRemove) {
+ TestDelegate delegate;
+ delegate.AddView(100);
+ delegate.AddView(200);
+ PointerDataPacketConverter converter(delegate);
+ auto packet = std::make_unique<PointerDataPacket>(3);
+
+ PointerData data;
+ CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0);
+ data.view_id = 100;
+ packet->SetPointerData(0, data);
+ CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 3.0, 4.0, 1);
+ data.view_id = 100;
+ packet->SetPointerData(1, data);
+ CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0);
+ data.view_id = 200;
+ packet->SetPointerData(2, data);
+ auto converted_packet = converter.Convert(*packet);
+
+ std::vector<PointerData> result;
+ UnpackPointerPacket(result, std::move(converted_packet));
+
+ ASSERT_EQ(result.size(), (size_t)6);
+ ASSERT_EQ(result[0].synthesized, 0);
+ ASSERT_EQ(result[0].view_id, 100);
+
+ // A hover should be synthesized.
+ ASSERT_EQ(result[1].change, PointerData::Change::kHover);
+ ASSERT_EQ(result[1].synthesized, 1);
+ ASSERT_EQ(result[1].physical_delta_x, 3.0);
+ ASSERT_EQ(result[1].physical_delta_y, 4.0);
+ ASSERT_EQ(result[1].buttons, 0);
+
+ ASSERT_EQ(result[2].change, PointerData::Change::kDown);
+ ASSERT_EQ(result[2].pointer_identifier, 1);
+ ASSERT_EQ(result[2].synthesized, 0);
+ ASSERT_EQ(result[2].buttons, 1);
+
+ // A cancel should be synthesized.
+ ASSERT_EQ(result[3].change, PointerData::Change::kCancel);
+ ASSERT_EQ(result[3].pointer_identifier, 1);
+ ASSERT_EQ(result[3].synthesized, 1);
+ ASSERT_EQ(result[3].physical_x, 3.0);
+ ASSERT_EQ(result[3].physical_y, 4.0);
+ ASSERT_EQ(result[3].buttons, 1);
+
+ // A remove should be synthesized.
+ ASSERT_EQ(result[4].physical_x, 3.0);
+ ASSERT_EQ(result[4].physical_y, 4.0);
+ ASSERT_EQ(result[4].synthesized, 1);
+ ASSERT_EQ(result[4].view_id, 100);
+
+ ASSERT_EQ(result[5].synthesized, 0);
+ ASSERT_EQ(result[5].view_id, 200);
+}
+
+TEST(PointerDataPacketConverterTest,
+ CanAvoidDoubleRemoveAfterSynthesizedRemove) {
+ TestDelegate delegate;
+ delegate.AddView(100);
+ delegate.AddView(200);
+ PointerDataPacketConverter converter(delegate);
+ auto packet = std::make_unique<PointerDataPacket>(2);
+
+ PointerData data;
+ CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0);
+ data.view_id = 100;
+ packet->SetPointerData(0, data);
+ CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0);
+ data.view_id = 200;
+ packet->SetPointerData(1, data);
+ auto converted_packet = converter.Convert(*packet);
+
+ std::vector<PointerData> result;
+ UnpackPointerPacket(result, std::move(converted_packet));
+
+ ASSERT_EQ(result.size(), (size_t)3);
+ ASSERT_EQ(result[0].synthesized, 0);
+ ASSERT_EQ(result[0].view_id, 100);
+
+ // A remove should be synthesized.
+ ASSERT_EQ(result[1].synthesized, 1);
+ ASSERT_EQ(result[1].view_id, 100);
+
+ ASSERT_EQ(result[2].synthesized, 0);
+ ASSERT_EQ(result[2].view_id, 200);
+
+ // Simulate a double remove.
+ packet = std::make_unique<PointerDataPacket>(1);
+ CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 0.0, 0.0,
+ 0);
+ data.view_id = 100;
+ packet->SetPointerData(0, data);
+ converted_packet = converter.Convert(*packet);
+
+ result.clear();
+ UnpackPointerPacket(result, std::move(converted_packet));
+
+ // The double remove should be ignored.
+ ASSERT_EQ(result.size(), (size_t)0);
+}
+
TEST(PointerDataPacketConverterTest, CanHandleThreeFingerGesture) {
// Regression test https://github.com/flutter/flutter/issues/20517.
TestDelegate delegate;
diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm
index 0013657..904a3ea 100644
--- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm
+++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm
@@ -2782,79 +2782,6 @@
flutterPlatformViewsController->Reset();
}
-- (void)testFlutterPlatformViewForwardingAndDelayingRecognizerFailureCondition {
- flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
-
- flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
- /*platform=*/GetDefaultTaskRunner(),
- /*raster=*/GetDefaultTaskRunner(),
- /*ui=*/GetDefaultTaskRunner(),
- /*io=*/GetDefaultTaskRunner());
- auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
- flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
- auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
- /*delegate=*/mock_delegate,
- /*rendering_api=*/mock_delegate.settings_.enable_impeller
- ? flutter::IOSRenderingAPI::kMetal
- : flutter::IOSRenderingAPI::kSoftware,
- /*platform_views_controller=*/flutterPlatformViewsController,
- /*task_runners=*/runners,
- /*worker_task_runner=*/nil,
- /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
-
- FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
- [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
- flutterPlatformViewsController->RegisterViewFactory(
- factory, @"MockFlutterPlatformView",
- FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
- FlutterResult result = ^(id result) {
- };
- flutterPlatformViewsController->OnMethodCall(
- [FlutterMethodCall
- methodCallWithMethodName:@"create"
- arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
- result);
-
- XCTAssertNotNil(gMockPlatformView);
-
- // Find touch inteceptor view
- UIView* touchInteceptorView = gMockPlatformView;
- while (touchInteceptorView != nil &&
- ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
- touchInteceptorView = touchInteceptorView.superview;
- }
- XCTAssertNotNil(touchInteceptorView);
-
- // Find ForwardGestureRecognizer
- UIGestureRecognizer* forwardingGestureRecognizer = nil;
- UIGestureRecognizer* delayingGestureRecognizer = nil;
- for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
- if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
- forwardingGestureRecognizer = gestureRecognizer;
- }
- if ([gestureRecognizer isKindOfClass:NSClassFromString(@"FlutterDelayingGestureRecognizer")]) {
- delayingGestureRecognizer = gestureRecognizer;
- }
- }
- UIGestureRecognizer* otherGestureRecognizer = OCMClassMock([UIGestureRecognizer class]);
-
- id flutterViewContoller = OCMClassMock([FlutterViewController class]);
- flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller);
-
- XCTAssertFalse([delayingGestureRecognizer.delegate
- gestureRecognizer:delayingGestureRecognizer
- shouldBeRequiredToFailByGestureRecognizer:forwardingGestureRecognizer]);
- XCTAssertTrue([delayingGestureRecognizer.delegate gestureRecognizer:delayingGestureRecognizer
- shouldBeRequiredToFailByGestureRecognizer:otherGestureRecognizer]);
-
- XCTAssertTrue([delayingGestureRecognizer.delegate gestureRecognizer:delayingGestureRecognizer
- shouldRequireFailureOfGestureRecognizer:forwardingGestureRecognizer]);
- XCTAssertFalse([delayingGestureRecognizer.delegate gestureRecognizer:delayingGestureRecognizer
- shouldRequireFailureOfGestureRecognizer:otherGestureRecognizer]);
-
- flutterPlatformViewsController->Reset();
-}
-
- (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing {
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm
index 90c4d42..5e76654 100644
--- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm
+++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm
@@ -654,14 +654,14 @@
- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
- // The forwarding gesture recognizer should always get all touch events, so it should not
- // require other gesture recognizer to fail.
- return otherGestureRecognizer != _forwardingRecognizer;
+ // The forwarding gesture recognizer should always get all touch events, so it should not be
+ // required to fail by any other gesture recognizer.
+ return otherGestureRecognizer != _forwardingRecognizer && otherGestureRecognizer != self;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
- return otherGestureRecognizer == _forwardingRecognizer;
+ return otherGestureRecognizer == self;
}
- (void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
diff --git a/shell/platform/fuchsia/dart/BUILD.gn b/shell/platform/fuchsia/dart/BUILD.gn
index b2140f8..b35defc 100644
--- a/shell/platform/fuchsia/dart/BUILD.gn
+++ b/shell/platform/fuchsia/dart/BUILD.gn
@@ -76,9 +76,13 @@
deps = []
sources = [
+ "async_helper.dart",
"config.dart",
"expect.dart",
+ "legacy/async_minitest.dart",
+ "legacy/minitest.dart",
"minitest.dart",
+ "static_type_helper.dart",
"variations.dart",
]
}
diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn
index 22c192e..a28e375 100644
--- a/shell/platform/linux/BUILD.gn
+++ b/shell/platform/linux/BUILD.gn
@@ -81,6 +81,7 @@
"fl_dart_project_private.h",
"fl_engine_private.h",
"fl_keyboard_handler.h",
+ "fl_keyboard_manager.h",
"fl_keyboard_pending_event.h",
"fl_keyboard_view_delegate.h",
"fl_key_event.h",
@@ -118,6 +119,7 @@
"fl_key_responder.cc",
"fl_keyboard_handler.cc",
"fl_keyboard_layout.cc",
+ "fl_keyboard_manager.cc",
"fl_keyboard_pending_event.cc",
"fl_keyboard_view_delegate.cc",
"fl_message_codec.cc",
@@ -217,6 +219,7 @@
"fl_key_embedder_responder_test.cc",
"fl_keyboard_handler_test.cc",
"fl_keyboard_layout_test.cc",
+ "fl_keyboard_manager_test.cc",
"fl_message_codec_test.cc",
"fl_method_channel_test.cc",
"fl_method_codec_test.cc",
diff --git a/shell/platform/linux/fl_keyboard_handler.cc b/shell/platform/linux/fl_keyboard_handler.cc
index e914925..266b9b1 100644
--- a/shell/platform/linux/fl_keyboard_handler.cc
+++ b/shell/platform/linux/fl_keyboard_handler.cc
@@ -4,376 +4,23 @@
#include "flutter/shell/platform/linux/fl_keyboard_handler.h"
-#include <array>
-#include <cinttypes>
-#include <memory>
-#include <string>
-
-#include "flutter/shell/platform/linux/fl_key_channel_responder.h"
-#include "flutter/shell/platform/linux/fl_key_embedder_responder.h"
-#include "flutter/shell/platform/linux/fl_keyboard_layout.h"
-#include "flutter/shell/platform/linux/fl_keyboard_pending_event.h"
-#include "flutter/shell/platform/linux/key_mapping.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
-// Turn on this flag to print complete layout data when switching IMEs. The data
-// is used in unit tests.
-#define DEBUG_PRINT_LAYOUT
-
static constexpr char kChannelName[] = "flutter/keyboard";
static constexpr char kGetKeyboardStateMethod[] = "getKeyboardState";
-/* Declarations of private classes */
-
-G_DECLARE_FINAL_TYPE(FlKeyboardHandlerUserData,
- fl_keyboard_handler_user_data,
- FL,
- KEYBOARD_HANDLER_USER_DATA,
- GObject);
-
-/* End declarations */
-
-namespace {
-
-static bool is_eascii(uint16_t character) {
- return character < 256;
-}
-
-#ifdef DEBUG_PRINT_LAYOUT
-// Prints layout entries that will be parsed by `MockLayoutData`.
-void debug_format_layout_data(std::string& debug_layout_data,
- uint16_t keycode,
- uint16_t clue1,
- uint16_t clue2) {
- if (keycode % 4 == 0) {
- debug_layout_data.append(" ");
- }
-
- constexpr int kBufferSize = 30;
- char buffer[kBufferSize];
- buffer[0] = 0;
- buffer[kBufferSize - 1] = 0;
-
- snprintf(buffer, kBufferSize, "0x%04x, 0x%04x, ", clue1, clue2);
- debug_layout_data.append(buffer);
-
- if (keycode % 4 == 3) {
- snprintf(buffer, kBufferSize, " // 0x%02x", keycode);
- debug_layout_data.append(buffer);
- }
-}
-#endif
-
-} // namespace
-
-/* Define FlKeyboardHandlerUserData */
-
-/**
- * FlKeyboardHandlerUserData:
- * The user_data used when #FlKeyboardHandler sends event to
- * responders.
- */
-
-struct _FlKeyboardHandlerUserData {
- GObject parent_instance;
-
- // The owner handler.
- GWeakRef handler;
- uint64_t sequence_id;
-};
-
-G_DEFINE_TYPE(FlKeyboardHandlerUserData,
- fl_keyboard_handler_user_data,
- G_TYPE_OBJECT)
-
-static void fl_keyboard_handler_user_data_dispose(GObject* object) {
- g_return_if_fail(FL_IS_KEYBOARD_HANDLER_USER_DATA(object));
- FlKeyboardHandlerUserData* self = FL_KEYBOARD_HANDLER_USER_DATA(object);
-
- g_weak_ref_clear(&self->handler);
-
- G_OBJECT_CLASS(fl_keyboard_handler_user_data_parent_class)->dispose(object);
-}
-
-static void fl_keyboard_handler_user_data_class_init(
- FlKeyboardHandlerUserDataClass* klass) {
- G_OBJECT_CLASS(klass)->dispose = fl_keyboard_handler_user_data_dispose;
-}
-
-static void fl_keyboard_handler_user_data_init(
- FlKeyboardHandlerUserData* self) {}
-
-// Creates a new FlKeyboardHandlerUserData private class with all information.
-static FlKeyboardHandlerUserData* fl_keyboard_handler_user_data_new(
- FlKeyboardHandler* handler,
- uint64_t sequence_id) {
- FlKeyboardHandlerUserData* self = FL_KEYBOARD_HANDLER_USER_DATA(
- g_object_new(fl_keyboard_handler_user_data_get_type(), nullptr));
-
- g_weak_ref_init(&self->handler, handler);
- self->sequence_id = sequence_id;
- return self;
-}
-
-/* Define FlKeyboardHandler */
-
struct _FlKeyboardHandler {
GObject parent_instance;
GWeakRef view_delegate;
- // An array of #FlKeyResponder. Elements are added with
- // #fl_keyboard_handler_add_responder immediately after initialization and are
- // automatically released on dispose.
- GPtrArray* responder_list;
-
- // An array of #FlKeyboardPendingEvent.
- //
- // Its elements are *not* unreferenced when removed. When FlKeyboardHandler is
- // disposed, this array will be set with a free_func so that the elements are
- // unreferenced when removed.
- GPtrArray* pending_responds;
-
- // An array of #FlKeyboardPendingEvent.
- //
- // Its elements are unreferenced when removed.
- GPtrArray* pending_redispatches;
-
- // The last sequence ID used. Increased by 1 by every use.
- uint64_t last_sequence_id;
-
- // Record the derived layout.
- //
- // It is cleared when the platform reports a layout switch. Each entry,
- // which corresponds to a group, is only initialized on the arrival of the
- // first event for that group that has a goal keycode.
- FlKeyboardLayout* derived_layout;
-
- // A static map from keycodes to all layout goals.
- //
- // It is set up when the handler is initialized and is not changed ever after.
- std::unique_ptr<std::map<uint16_t, const LayoutGoal*>> keycode_to_goals;
-
- // A static map from logical keys to all mandatory layout goals.
- //
- // It is set up when the handler is initialized and is not changed ever after.
- std::unique_ptr<std::map<uint64_t, const LayoutGoal*>>
- logical_to_mandatory_goals;
-
// The channel used by the framework to query the keyboard pressed state.
FlMethodChannel* channel;
};
G_DEFINE_TYPE(FlKeyboardHandler, fl_keyboard_handler, G_TYPE_OBJECT);
-// This is an exact copy of g_ptr_array_find_with_equal_func. Somehow CI
-// reports that can not find symbol g_ptr_array_find_with_equal_func, despite
-// the fact that it runs well locally.
-static gboolean g_ptr_array_find_with_equal_func1(GPtrArray* haystack,
- gconstpointer needle,
- GEqualFunc equal_func,
- guint* index_) {
- guint i;
- g_return_val_if_fail(haystack != NULL, FALSE);
- if (equal_func == NULL) {
- equal_func = g_direct_equal;
- }
- for (i = 0; i < haystack->len; i++) {
- if (equal_func(g_ptr_array_index(haystack, i), needle)) {
- if (index_ != NULL) {
- *index_ = i;
- }
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-// Compare a #FlKeyboardPendingEvent with the given sequence_id.
-static gboolean compare_pending_by_sequence_id(gconstpointer a,
- gconstpointer b) {
- FlKeyboardPendingEvent* pending =
- FL_KEYBOARD_PENDING_EVENT(const_cast<gpointer>(a));
- uint64_t sequence_id = *reinterpret_cast<const uint64_t*>(b);
- return fl_keyboard_pending_event_get_sequence_id(pending) == sequence_id;
-}
-
-// Compare a #FlKeyboardPendingEvent with the given hash.
-static gboolean compare_pending_by_hash(gconstpointer a, gconstpointer b) {
- FlKeyboardPendingEvent* pending =
- FL_KEYBOARD_PENDING_EVENT(const_cast<gpointer>(a));
- uint64_t hash = *reinterpret_cast<const uint64_t*>(b);
- return fl_keyboard_pending_event_get_hash(pending) == hash;
-}
-
-// Try to remove a pending event from `pending_redispatches` with the target
-// hash.
-//
-// Returns true if the event is found and removed.
-static bool fl_keyboard_handler_remove_redispatched(FlKeyboardHandler* self,
- uint64_t hash) {
- guint result_index;
- gboolean found = g_ptr_array_find_with_equal_func1(
- self->pending_redispatches, static_cast<const uint64_t*>(&hash),
- compare_pending_by_hash, &result_index);
- if (found) {
- // The removed object is freed due to `pending_redispatches`'s free_func.
- g_ptr_array_remove_index_fast(self->pending_redispatches, result_index);
- return TRUE;
- } else {
- return FALSE;
- }
-}
-
-// The callback used by a responder after the event was dispatched.
-static void responder_handle_event_callback(bool handled,
- gpointer user_data_ptr) {
- g_return_if_fail(FL_IS_KEYBOARD_HANDLER_USER_DATA(user_data_ptr));
- FlKeyboardHandlerUserData* user_data =
- FL_KEYBOARD_HANDLER_USER_DATA(user_data_ptr);
-
- g_autoptr(FlKeyboardHandler) self =
- FL_KEYBOARD_HANDLER(g_weak_ref_get(&user_data->handler));
- if (self == nullptr) {
- return;
- }
-
- g_autoptr(FlKeyboardViewDelegate) view_delegate =
- FL_KEYBOARD_VIEW_DELEGATE(g_weak_ref_get(&self->view_delegate));
- if (view_delegate == nullptr) {
- return;
- }
-
- guint result_index = -1;
- gboolean found = g_ptr_array_find_with_equal_func1(
- self->pending_responds, &user_data->sequence_id,
- compare_pending_by_sequence_id, &result_index);
- g_return_if_fail(found);
- FlKeyboardPendingEvent* pending = FL_KEYBOARD_PENDING_EVENT(
- g_ptr_array_index(self->pending_responds, result_index));
- g_return_if_fail(pending != nullptr);
- fl_keyboard_pending_event_mark_replied(pending, handled);
- // All responders have replied.
- if (fl_keyboard_pending_event_is_complete(pending)) {
- g_object_unref(user_data_ptr);
- gpointer removed =
- g_ptr_array_remove_index_fast(self->pending_responds, result_index);
- g_return_if_fail(removed == pending);
- bool should_redispatch =
- !fl_keyboard_pending_event_get_any_handled(pending) &&
- !fl_keyboard_view_delegate_text_filter_key_press(
- view_delegate, fl_keyboard_pending_event_get_event(pending));
- if (should_redispatch) {
- g_ptr_array_add(self->pending_redispatches, pending);
- fl_keyboard_view_delegate_redispatch_event(
- view_delegate,
- FL_KEY_EVENT(fl_keyboard_pending_event_get_event(pending)));
- } else {
- g_object_unref(pending);
- }
- }
-}
-
-static uint16_t convert_key_to_char(FlKeyboardViewDelegate* view_delegate,
- guint keycode,
- gint group,
- gint level) {
- GdkKeymapKey key = {keycode, group, level};
- constexpr int kBmpMax = 0xD7FF;
- guint origin = fl_keyboard_view_delegate_lookup_key(view_delegate, &key);
- return origin < kBmpMax ? origin : 0xFFFF;
-}
-
-// Make sure that Flutter has derived the layout for the group of the event,
-// if the event contains a goal keycode.
-static void guarantee_layout(FlKeyboardHandler* self, FlKeyEvent* event) {
- g_autoptr(FlKeyboardViewDelegate) view_delegate =
- FL_KEYBOARD_VIEW_DELEGATE(g_weak_ref_get(&self->view_delegate));
- if (view_delegate == nullptr) {
- return;
- }
-
- guint8 group = fl_key_event_get_group(event);
- if (fl_keyboard_layout_has_group(self->derived_layout, group)) {
- return;
- }
- if (self->keycode_to_goals->find(fl_key_event_get_keycode(event)) ==
- self->keycode_to_goals->end()) {
- return;
- }
-
- // Clone all mandatory goals. Each goal is removed from this cloned map when
- // fulfilled, and the remaining ones will be assigned to a default position.
- std::map<uint64_t, const LayoutGoal*> remaining_mandatory_goals =
- *self->logical_to_mandatory_goals;
-
-#ifdef DEBUG_PRINT_LAYOUT
- std::string debug_layout_data;
- for (uint16_t keycode = 0; keycode < 128; keycode += 1) {
- std::vector<uint16_t> this_key_clues = {
- convert_key_to_char(view_delegate, keycode, group, 0),
- convert_key_to_char(view_delegate, keycode, group, 1), // Shift
- };
- debug_format_layout_data(debug_layout_data, keycode, this_key_clues[0],
- this_key_clues[1]);
- }
-#endif
-
- // It's important to only traverse layout goals instead of all keycodes.
- // Some key codes outside of the standard keyboard also gives alpha-numeric
- // letters, and will therefore take over mandatory goals from standard
- // keyboard keys if they come first. Example: French keyboard digit 1.
- for (const LayoutGoal& keycode_goal : layout_goals) {
- uint16_t keycode = keycode_goal.keycode;
- std::vector<uint16_t> this_key_clues = {
- convert_key_to_char(view_delegate, keycode, group, 0),
- convert_key_to_char(view_delegate, keycode, group, 1), // Shift
- };
-
- // The logical key should be the first available clue from below:
- //
- // - Mandatory goal, if it matches any clue. This ensures that all alnum
- // keys can be found somewhere.
- // - US layout, if neither clue of the key is EASCII. This ensures that
- // there are no non-latin logical keys.
- // - A value derived on the fly from keycode & keyval.
- for (uint16_t clue : this_key_clues) {
- auto matching_goal = remaining_mandatory_goals.find(clue);
- if (matching_goal != remaining_mandatory_goals.end()) {
- // Found a key that produces a mandatory char. Use it.
- g_return_if_fail(fl_keyboard_layout_get_logical_key(
- self->derived_layout, group, keycode) == 0);
- fl_keyboard_layout_set_logical_key(self->derived_layout, group, keycode,
- clue);
- remaining_mandatory_goals.erase(matching_goal);
- break;
- }
- }
- bool has_any_eascii =
- is_eascii(this_key_clues[0]) || is_eascii(this_key_clues[1]);
- // See if any produced char meets the requirement as a logical key.
- if (fl_keyboard_layout_get_logical_key(self->derived_layout, group,
- keycode) == 0 &&
- !has_any_eascii) {
- auto found_us_layout = self->keycode_to_goals->find(keycode);
- if (found_us_layout != self->keycode_to_goals->end()) {
- fl_keyboard_layout_set_logical_key(
- self->derived_layout, group, keycode,
- found_us_layout->second->logical_key);
- }
- }
- }
-
- // Ensure all mandatory goals are assigned.
- for (const auto mandatory_goal_iter : remaining_mandatory_goals) {
- const LayoutGoal* goal = mandatory_goal_iter.second;
- fl_keyboard_layout_set_logical_key(self->derived_layout, group,
- goal->keycode, goal->logical_key);
- }
-}
-
// Returns the keyboard pressed state.
static FlMethodResponse* get_keyboard_state(FlKeyboardHandler* self) {
g_autoptr(FlValue) result = fl_value_new_map();
@@ -423,15 +70,7 @@
FlKeyboardHandler* self = FL_KEYBOARD_HANDLER(object);
g_weak_ref_clear(&self->view_delegate);
-
- self->keycode_to_goals.reset();
- self->logical_to_mandatory_goals.reset();
-
- g_ptr_array_free(self->responder_list, TRUE);
- g_ptr_array_set_free_func(self->pending_responds, g_object_unref);
- g_ptr_array_free(self->pending_responds, TRUE);
- g_ptr_array_free(self->pending_redispatches, TRUE);
- g_clear_object(&self->derived_layout);
+ g_clear_object(&self->channel);
G_OBJECT_CLASS(fl_keyboard_handler_parent_class)->dispose(object);
}
@@ -440,27 +79,7 @@
G_OBJECT_CLASS(klass)->dispose = fl_keyboard_handler_dispose;
}
-static void fl_keyboard_handler_init(FlKeyboardHandler* self) {
- self->derived_layout = fl_keyboard_layout_new();
-
- self->keycode_to_goals =
- std::make_unique<std::map<uint16_t, const LayoutGoal*>>();
- self->logical_to_mandatory_goals =
- std::make_unique<std::map<uint64_t, const LayoutGoal*>>();
- for (const LayoutGoal& goal : layout_goals) {
- (*self->keycode_to_goals)[goal.keycode] = &goal;
- if (goal.mandatory) {
- (*self->logical_to_mandatory_goals)[goal.logical_key] = &goal;
- }
- }
-
- self->responder_list = g_ptr_array_new_with_free_func(g_object_unref);
-
- self->pending_responds = g_ptr_array_new();
- self->pending_redispatches = g_ptr_array_new_with_free_func(g_object_unref);
-
- self->last_sequence_id = 1;
-}
+static void fl_keyboard_handler_init(FlKeyboardHandler* self) {}
FlKeyboardHandler* fl_keyboard_handler_new(
FlBinaryMessenger* messenger,
@@ -472,27 +91,6 @@
g_weak_ref_init(&self->view_delegate, view_delegate);
- // The embedder responder must be added before the channel responder.
- g_ptr_array_add(
- self->responder_list,
- FL_KEY_RESPONDER(fl_key_embedder_responder_new(
- [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
- void* callback_user_data, void* send_key_event_user_data) {
- FlKeyboardHandler* self =
- FL_KEYBOARD_HANDLER(send_key_event_user_data);
- g_autoptr(FlKeyboardViewDelegate) view_delegate =
- FL_KEYBOARD_VIEW_DELEGATE(g_weak_ref_get(&self->view_delegate));
- if (view_delegate == nullptr) {
- return;
- }
- fl_keyboard_view_delegate_send_key_event(
- view_delegate, event, callback, callback_user_data);
- },
- self)));
- g_ptr_array_add(self->responder_list,
- FL_KEY_RESPONDER(fl_key_channel_responder_new(
- fl_keyboard_view_delegate_get_messenger(view_delegate))));
-
// Setup the flutter/keyboard channel.
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
self->channel =
@@ -501,70 +99,3 @@
self, nullptr);
return self;
}
-
-gboolean fl_keyboard_handler_handle_event(FlKeyboardHandler* self,
- FlKeyEvent* event) {
- g_return_val_if_fail(FL_IS_KEYBOARD_HANDLER(self), FALSE);
- g_return_val_if_fail(event != nullptr, FALSE);
-
- guarantee_layout(self, event);
-
- uint64_t incoming_hash = fl_key_event_hash(event);
- if (fl_keyboard_handler_remove_redispatched(self, incoming_hash)) {
- return FALSE;
- }
-
- FlKeyboardPendingEvent* pending = fl_keyboard_pending_event_new(
- event, ++self->last_sequence_id, self->responder_list->len);
-
- g_ptr_array_add(self->pending_responds, pending);
- FlKeyboardHandlerUserData* user_data = fl_keyboard_handler_user_data_new(
- self, fl_keyboard_pending_event_get_sequence_id(pending));
- uint64_t specified_logical_key = fl_keyboard_layout_get_logical_key(
- self->derived_layout, fl_key_event_get_group(event),
- fl_key_event_get_keycode(event));
- for (guint i = 0; i < self->responder_list->len; i++) {
- FlKeyResponder* responder =
- FL_KEY_RESPONDER(g_ptr_array_index(self->responder_list, i));
- fl_key_responder_handle_event(responder, event,
- responder_handle_event_callback, user_data,
- specified_logical_key);
- }
-
- return TRUE;
-}
-
-gboolean fl_keyboard_handler_is_state_clear(FlKeyboardHandler* self) {
- g_return_val_if_fail(FL_IS_KEYBOARD_HANDLER(self), FALSE);
- return self->pending_responds->len == 0 &&
- self->pending_redispatches->len == 0;
-}
-
-void fl_keyboard_handler_sync_modifier_if_needed(FlKeyboardHandler* self,
- guint state,
- double event_time) {
- g_return_if_fail(FL_IS_KEYBOARD_HANDLER(self));
-
- // The embedder responder is the first element in
- // FlKeyboardHandler.responder_list.
- FlKeyEmbedderResponder* responder =
- FL_KEY_EMBEDDER_RESPONDER(g_ptr_array_index(self->responder_list, 0));
- fl_key_embedder_responder_sync_modifiers_if_needed(responder, state,
- event_time);
-}
-
-GHashTable* fl_keyboard_handler_get_pressed_state(FlKeyboardHandler* self) {
- g_return_val_if_fail(FL_IS_KEYBOARD_HANDLER(self), nullptr);
-
- // The embedder responder is the first element in
- // FlKeyboardHandler.responder_list.
- FlKeyEmbedderResponder* responder =
- FL_KEY_EMBEDDER_RESPONDER(g_ptr_array_index(self->responder_list, 0));
- return fl_key_embedder_responder_get_pressed_state(responder);
-}
-
-void fl_keyboard_handler_notify_layout_changed(FlKeyboardHandler* self) {
- g_return_if_fail(FL_IS_KEYBOARD_HANDLER(self));
- g_clear_object(&self->derived_layout);
- self->derived_layout = fl_keyboard_layout_new();
-}
diff --git a/shell/platform/linux/fl_keyboard_handler.h b/shell/platform/linux/fl_keyboard_handler.h
index 0d551d6..57da5e9 100644
--- a/shell/platform/linux/fl_keyboard_handler.h
+++ b/shell/platform/linux/fl_keyboard_handler.h
@@ -8,6 +8,7 @@
#include <gdk/gdk.h>
#include "flutter/shell/platform/linux/fl_keyboard_view_delegate.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"
G_BEGIN_DECLS
@@ -21,17 +22,7 @@
/**
* FlKeyboardHandler:
*
- * Processes keyboard events and cooperate with `TextInputHandler`.
- *
- * A keyboard event goes through a few sections, each can choose to handle the
- * event, and only unhandled events can move to the next section:
- *
- * - Keyboard: Dispatch to the embedder responder and the channel responder
- * simultaneously. After both responders have responded (asynchronously), the
- * event is considered handled if either responder handles it.
- * - Text input: Events are sent to IM filter (usually owned by
- * `TextInputHandler`) and are handled synchronously.
- * - Redispatching: Events are inserted back to the system for redispatching.
+ * Provides the channel to receive keyboard requests from the Dart code.
*/
/**
@@ -47,60 +38,6 @@
FlBinaryMessenger* messenger,
FlKeyboardViewDelegate* view_delegate);
-/**
- * fl_keyboard_handler_handle_event:
- * @handler: the #FlKeyboardHandler self.
- * @event: the event to be dispatched. It is usually a wrap of a GdkEventKey.
- * This event will be managed and released by #FlKeyboardHandler.
- *
- * Make the handler process a system key event. This might eventually send
- * messages to the framework, trigger text input effects, or redispatch the
- * event back to the system.
- */
-gboolean fl_keyboard_handler_handle_event(FlKeyboardHandler* handler,
- FlKeyEvent* event);
-
-/**
- * fl_keyboard_handler_is_state_clear:
- * @handler: the #FlKeyboardHandler self.
- *
- * A debug-only method that queries whether the handler's various states are
- * cleared, i.e. no pending events for redispatching or for responding.
- *
- * Returns: true if the handler's various states are cleared.
- */
-gboolean fl_keyboard_handler_is_state_clear(FlKeyboardHandler* handler);
-
-/**
- * fl_keyboard_handler_sync_modifier_if_needed:
- * @handler: the #FlKeyboardHandler self.
- * @state: the state of the modifiers mask.
- * @event_time: the time attribute of the incoming GDK event.
- *
- * If needed, synthesize modifier keys up and down event by comparing their
- * current pressing states with the given modifiers mask.
- */
-void fl_keyboard_handler_sync_modifier_if_needed(FlKeyboardHandler* handler,
- guint state,
- double event_time);
-
-/**
- * fl_keyboard_handler_get_pressed_state:
- * @handler: the #FlKeyboardHandler self.
- *
- * Returns the keyboard pressed state. The hash table contains one entry per
- * pressed keys, mapping from the logical key to the physical key.*
- */
-GHashTable* fl_keyboard_handler_get_pressed_state(FlKeyboardHandler* handler);
-
-/**
- * fl_keyboard_handler_notify_layout_changed:
- * @handler: the #FlKeyboardHandler self.
- *
- * Notify the handler the keyboard layout has changed.
- */
-void fl_keyboard_handler_notify_layout_changed(FlKeyboardHandler* handler);
-
G_END_DECLS
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_KEYBOARD_HANDLER_H_
diff --git a/shell/platform/linux/fl_keyboard_handler_test.cc b/shell/platform/linux/fl_keyboard_handler_test.cc
index f0e9815..a4d9278 100644
--- a/shell/platform/linux/fl_keyboard_handler_test.cc
+++ b/shell/platform/linux/fl_keyboard_handler_test.cc
@@ -4,143 +4,26 @@
#include "flutter/shell/platform/linux/fl_keyboard_handler.h"
-#include <cstring>
-#include <vector>
-
-#include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
-#include "flutter/shell/platform/linux/fl_binary_messenger_private.h"
#include "flutter/shell/platform/linux/fl_method_codec_private.h"
-#include "flutter/shell/platform/linux/key_mapping.h"
-#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
-#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
-#include "flutter/shell/platform/linux/testing/fl_test.h"
#include "flutter/shell/platform/linux/testing/mock_binary_messenger.h"
-#include "flutter/shell/platform/linux/testing/mock_text_input_handler.h"
-#include "flutter/testing/testing.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
-// Define compound `expect` in macros. If they were defined in functions, the
-// stacktrace wouldn't print where the function is called in the unit tests.
-
-#define EXPECT_KEY_EVENT(RECORD, TYPE, PHYSICAL, LOGICAL, CHAR, SYNTHESIZED) \
- EXPECT_EQ((RECORD).type, CallRecord::kKeyCallEmbedder); \
- EXPECT_EQ((RECORD).event->type, (TYPE)); \
- EXPECT_EQ((RECORD).event->physical, (PHYSICAL)); \
- EXPECT_EQ((RECORD).event->logical, (LOGICAL)); \
- EXPECT_STREQ((RECORD).event->character, (CHAR)); \
- EXPECT_EQ((RECORD).event->synthesized, (SYNTHESIZED));
-
-#define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR) \
- EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder); \
- EXPECT_EQ(call_records[0].event->type, kFlutterKeyEventTypeDown); \
- EXPECT_EQ(call_records[0].event->logical, (OUT_LOGICAL)); \
- EXPECT_STREQ(call_records[0].event->character, (OUT_CHAR)); \
- EXPECT_EQ(call_records[0].event->synthesized, false); \
- call_records.clear()
-
-namespace {
-using ::flutter::testing::keycodes::kLogicalAltLeft;
-using ::flutter::testing::keycodes::kLogicalBracketLeft;
-using ::flutter::testing::keycodes::kLogicalComma;
-using ::flutter::testing::keycodes::kLogicalControlLeft;
-using ::flutter::testing::keycodes::kLogicalDigit1;
-using ::flutter::testing::keycodes::kLogicalKeyA;
-using ::flutter::testing::keycodes::kLogicalKeyB;
-using ::flutter::testing::keycodes::kLogicalKeyM;
-using ::flutter::testing::keycodes::kLogicalKeyQ;
-using ::flutter::testing::keycodes::kLogicalMetaLeft;
-using ::flutter::testing::keycodes::kLogicalMinus;
-using ::flutter::testing::keycodes::kLogicalParenthesisRight;
-using ::flutter::testing::keycodes::kLogicalSemicolon;
-using ::flutter::testing::keycodes::kLogicalShiftLeft;
-using ::flutter::testing::keycodes::kLogicalUnderscore;
-
-using ::flutter::testing::keycodes::kPhysicalAltLeft;
-using ::flutter::testing::keycodes::kPhysicalControlLeft;
-using ::flutter::testing::keycodes::kPhysicalKeyA;
-using ::flutter::testing::keycodes::kPhysicalKeyB;
-using ::flutter::testing::keycodes::kPhysicalMetaLeft;
-using ::flutter::testing::keycodes::kPhysicalShiftLeft;
-
-// Hardware key codes.
-typedef std::function<void(bool handled)> AsyncKeyCallback;
-typedef std::function<void(AsyncKeyCallback callback)> ChannelCallHandler;
-typedef std::function<void(const FlutterKeyEvent* event,
- AsyncKeyCallback callback)>
- EmbedderCallHandler;
-typedef std::function<void(FlKeyEvent*)> RedispatchHandler;
-
-// A type that can record all kinds of effects that the keyboard handler
-// triggers.
-//
-// An instance of `CallRecord` might not have all the fields filled.
-typedef struct {
- enum {
- kKeyCallEmbedder,
- kKeyCallChannel,
- } type;
-
- AsyncKeyCallback callback;
- std::unique_ptr<FlutterKeyEvent> event;
- std::unique_ptr<char[]> event_character;
-} CallRecord;
-
-// Clone a C-string.
-//
-// Must be deleted by delete[].
-char* cloneString(const char* source) {
- if (source == nullptr) {
- return nullptr;
- }
- size_t charLen = strlen(source);
- char* target = new char[charLen + 1];
- strncpy(target, source, charLen + 1);
- return target;
-}
-
-constexpr guint16 kKeyCodeKeyA = 0x26u;
-constexpr guint16 kKeyCodeKeyB = 0x38u;
-constexpr guint16 kKeyCodeKeyM = 0x3au;
-constexpr guint16 kKeyCodeDigit1 = 0x0au;
-constexpr guint16 kKeyCodeMinus = 0x14u;
-constexpr guint16 kKeyCodeSemicolon = 0x2fu;
-constexpr guint16 kKeyCodeKeyLeftBracket = 0x22u;
-
-static constexpr char kKeyEventChannelName[] = "flutter/keyevent";
static constexpr char kKeyboardChannelName[] = "flutter/keyboard";
static constexpr char kGetKeyboardStateMethod[] = "getKeyboardState";
static constexpr uint64_t kMockPhysicalKey = 42;
static constexpr uint64_t kMockLogicalKey = 42;
-// All key clues for a keyboard layout.
-//
-// The index is (keyCode * 2 + hasShift), where each value is the character for
-// this key (GTK only supports UTF-16.) Since the maximum keycode of interest
-// is 128, it has a total of 256 entries..
-typedef std::array<uint32_t, 256> MockGroupLayoutData;
-typedef std::vector<const MockGroupLayoutData*> MockLayoutData;
-
-extern const MockLayoutData kLayoutUs;
-extern const MockLayoutData kLayoutRussian;
-extern const MockLayoutData kLayoutFrench;
-
G_BEGIN_DECLS
-G_DECLARE_FINAL_TYPE(FlMockViewDelegate,
- fl_mock_view_delegate,
+G_DECLARE_FINAL_TYPE(FlMockKeyboardHandlerDelegate,
+ fl_mock_keyboard_handler_delegate,
FL,
- MOCK_VIEW_DELEGATE,
+ MOCK_KEYBOARD_HANDLER_DELEGATE,
GObject);
-G_DECLARE_FINAL_TYPE(FlMockKeyBinaryMessenger,
- fl_mock_key_binary_messenger,
- FL,
- MOCK_KEY_BINARY_MESSENGER,
- GObject)
-
G_END_DECLS
MATCHER_P(MethodSuccessResponse, result, "") {
@@ -156,243 +39,26 @@
return false;
}
-/***** FlMockKeyBinaryMessenger *****/
-/* Mock a binary messenger that only processes messages from the embedding on
- * the key event channel, and does so according to the callback set by
- * fl_mock_key_binary_messenger_set_callback_handler */
-
-struct _FlMockKeyBinaryMessenger {
+struct _FlMockKeyboardHandlerDelegate {
GObject parent_instance;
};
-struct FlMockKeyBinaryMessengerPrivate {
- ChannelCallHandler callback_handler;
-};
-
-static void fl_mock_key_binary_messenger_iface_init(
- FlBinaryMessengerInterface* iface);
-
-G_DEFINE_TYPE_WITH_CODE(
- FlMockKeyBinaryMessenger,
- fl_mock_key_binary_messenger,
- G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE(fl_binary_messenger_get_type(),
- fl_mock_key_binary_messenger_iface_init);
- G_ADD_PRIVATE(FlMockKeyBinaryMessenger))
-
-#define FL_MOCK_KEY_BINARY_MESSENGER_GET_PRIVATE(obj) \
- static_cast<FlMockKeyBinaryMessengerPrivate*>( \
- fl_mock_key_binary_messenger_get_instance_private( \
- FL_MOCK_KEY_BINARY_MESSENGER(obj)))
-
-static void fl_mock_key_binary_messenger_init(FlMockKeyBinaryMessenger* self) {
- FlMockKeyBinaryMessengerPrivate* priv =
- FL_MOCK_KEY_BINARY_MESSENGER_GET_PRIVATE(self);
- new (priv) FlMockKeyBinaryMessengerPrivate();
-}
-
-static void fl_mock_key_binary_messenger_dispose(GObject* object) {
- FL_MOCK_KEY_BINARY_MESSENGER_GET_PRIVATE(object)
- ->~FlMockKeyBinaryMessengerPrivate();
-
- G_OBJECT_CLASS(fl_mock_key_binary_messenger_parent_class)->dispose(object);
-}
-
-static void fl_mock_key_binary_messenger_class_init(
- FlMockKeyBinaryMessengerClass* klass) {
- G_OBJECT_CLASS(klass)->dispose = fl_mock_key_binary_messenger_dispose;
-}
-
-static void fl_mock_key_binary_messenger_send_on_channel(
- FlBinaryMessenger* messenger,
- const gchar* channel,
- GBytes* message,
- GCancellable* cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data) {
- FlMockKeyBinaryMessenger* self = FL_MOCK_KEY_BINARY_MESSENGER(messenger);
-
- if (callback != nullptr) {
- EXPECT_STREQ(channel, kKeyEventChannelName);
- FL_MOCK_KEY_BINARY_MESSENGER_GET_PRIVATE(self)->callback_handler(
- [self, cancellable, callback, user_data](bool handled) {
- g_autoptr(GTask) task =
- g_task_new(self, cancellable, callback, user_data);
- g_autoptr(FlValue) result = fl_value_new_map();
- fl_value_set_string_take(result, "handled",
- fl_value_new_bool(handled));
- g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
- g_autoptr(GError) error = nullptr;
- GBytes* data = fl_message_codec_encode_message(
- FL_MESSAGE_CODEC(codec), result, &error);
-
- g_task_return_pointer(
- task, data, reinterpret_cast<GDestroyNotify>(g_bytes_unref));
- });
- }
-}
-
-static GBytes* fl_mock_key_binary_messenger_send_on_channel_finish(
- FlBinaryMessenger* messenger,
- GAsyncResult* result,
- GError** error) {
- return static_cast<GBytes*>(g_task_propagate_pointer(G_TASK(result), error));
-}
-
-static void fl_mock_binary_messenger_resize_channel(
- FlBinaryMessenger* messenger,
- const gchar* channel,
- int64_t new_size) {
- // Mock implementation. Do nothing.
-}
-
-static void fl_mock_binary_messenger_set_warns_on_channel_overflow(
- FlBinaryMessenger* messenger,
- const gchar* channel,
- bool warns) {
- // Mock implementation. Do nothing.
-}
-
-static void fl_mock_key_binary_messenger_iface_init(
- FlBinaryMessengerInterface* iface) {
- iface->set_message_handler_on_channel =
- [](FlBinaryMessenger* messenger, const gchar* channel,
- FlBinaryMessengerMessageHandler handler, gpointer user_data,
- GDestroyNotify destroy_notify) {
- EXPECT_STREQ(channel, kKeyEventChannelName);
- // No need to mock. The key event channel expects no incoming messages
- // from the framework.
- };
- iface->send_response = [](FlBinaryMessenger* messenger,
- FlBinaryMessengerResponseHandle* response_handle,
- GBytes* response, GError** error) -> gboolean {
- // The key event channel expects no incoming messages from the framework,
- // hence no responses either.
- g_return_val_if_reached(TRUE);
- return TRUE;
- };
- iface->send_on_channel = fl_mock_key_binary_messenger_send_on_channel;
- iface->send_on_channel_finish =
- fl_mock_key_binary_messenger_send_on_channel_finish;
- iface->resize_channel = fl_mock_binary_messenger_resize_channel;
- iface->set_warns_on_channel_overflow =
- fl_mock_binary_messenger_set_warns_on_channel_overflow;
-}
-
-static FlMockKeyBinaryMessenger* fl_mock_key_binary_messenger_new() {
- FlMockKeyBinaryMessenger* self = FL_MOCK_KEY_BINARY_MESSENGER(
- g_object_new(fl_mock_key_binary_messenger_get_type(), NULL));
-
- // Added to stop compiler complaining about an unused function.
- FL_IS_MOCK_KEY_BINARY_MESSENGER(self);
-
- return self;
-}
-
-static void fl_mock_key_binary_messenger_set_callback_handler(
- FlMockKeyBinaryMessenger* self,
- ChannelCallHandler handler) {
- FL_MOCK_KEY_BINARY_MESSENGER_GET_PRIVATE(self)->callback_handler =
- std::move(handler);
-}
-
-/***** FlMockViewDelegate *****/
-
-struct _FlMockViewDelegate {
- GObject parent_instance;
-};
-
-struct FlMockViewDelegatePrivate {
- FlMockKeyBinaryMessenger* messenger;
- EmbedderCallHandler embedder_handler;
- bool text_filter_result;
- RedispatchHandler redispatch_handler;
- const MockLayoutData* layout_data;
-};
-
-static void fl_mock_view_keyboard_delegate_iface_init(
+static void fl_mock_keyboard_handler_delegate_keyboard_view_delegate_iface_init(
FlKeyboardViewDelegateInterface* iface);
G_DEFINE_TYPE_WITH_CODE(
- FlMockViewDelegate,
- fl_mock_view_delegate,
+ FlMockKeyboardHandlerDelegate,
+ fl_mock_keyboard_handler_delegate,
G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE(fl_keyboard_view_delegate_get_type(),
- fl_mock_view_keyboard_delegate_iface_init);
- G_ADD_PRIVATE(FlMockViewDelegate))
+ G_IMPLEMENT_INTERFACE(
+ fl_keyboard_view_delegate_get_type(),
+ fl_mock_keyboard_handler_delegate_keyboard_view_delegate_iface_init))
-#define FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(obj) \
- static_cast<FlMockViewDelegatePrivate*>( \
- fl_mock_view_delegate_get_instance_private(FL_MOCK_VIEW_DELEGATE(obj)))
+static void fl_mock_keyboard_handler_delegate_init(
+ FlMockKeyboardHandlerDelegate* self) {}
-static void fl_mock_view_delegate_init(FlMockViewDelegate* self) {
- FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(self);
- new (priv) FlMockViewDelegatePrivate();
-}
-
-static void fl_mock_view_delegate_dispose(GObject* object) {
- FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(object);
-
- FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(object)->~FlMockViewDelegatePrivate();
- g_clear_object(&priv->messenger);
-
- G_OBJECT_CLASS(fl_mock_view_delegate_parent_class)->dispose(object);
-}
-
-static void fl_mock_view_delegate_class_init(FlMockViewDelegateClass* klass) {
- G_OBJECT_CLASS(klass)->dispose = fl_mock_view_delegate_dispose;
-}
-
-static void fl_mock_view_keyboard_send_key_event(
- FlKeyboardViewDelegate* view_delegate,
- const FlutterKeyEvent* event,
- FlutterKeyEventCallback callback,
- void* user_data) {
- FlMockViewDelegatePrivate* priv =
- FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(view_delegate);
- priv->embedder_handler(event, [callback, user_data](bool handled) {
- if (callback != nullptr) {
- callback(handled, user_data);
- }
- });
-}
-
-static gboolean fl_mock_view_keyboard_text_filter_key_press(
- FlKeyboardViewDelegate* view_delegate,
- FlKeyEvent* event) {
- FlMockViewDelegatePrivate* priv =
- FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(view_delegate);
- return priv->text_filter_result;
-}
-
-static FlBinaryMessenger* fl_mock_view_keyboard_get_messenger(
- FlKeyboardViewDelegate* view_delegate) {
- FlMockViewDelegatePrivate* priv =
- FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(view_delegate);
- return FL_BINARY_MESSENGER(priv->messenger);
-}
-
-static void fl_mock_view_keyboard_redispatch_event(
- FlKeyboardViewDelegate* view_delegate,
- FlKeyEvent* event) {
- FlMockViewDelegatePrivate* priv =
- FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(view_delegate);
- if (priv->redispatch_handler) {
- priv->redispatch_handler(event);
- }
-}
-
-static guint fl_mock_view_keyboard_lookup_key(FlKeyboardViewDelegate* delegate,
- const GdkKeymapKey* key) {
- FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(delegate);
- guint8 group = static_cast<guint8>(key->group);
- EXPECT_LT(group, priv->layout_data->size());
- const MockGroupLayoutData* group_layout = (*priv->layout_data)[group];
- EXPECT_TRUE(group_layout != nullptr);
- EXPECT_TRUE(key->level == 0 || key->level == 1);
- bool shift = key->level == 1;
- return (*group_layout)[key->keycode * 2 + shift];
-}
+static void fl_mock_keyboard_handler_delegate_class_init(
+ FlMockKeyboardHandlerDelegateClass* klass) {}
static GHashTable* fl_mock_view_keyboard_get_keyboard_state(
FlKeyboardViewDelegate* view_delegate) {
@@ -403,620 +69,27 @@
return result;
}
-static void fl_mock_view_keyboard_delegate_iface_init(
+static void fl_mock_keyboard_handler_delegate_keyboard_view_delegate_iface_init(
FlKeyboardViewDelegateInterface* iface) {
- iface->send_key_event = fl_mock_view_keyboard_send_key_event;
- iface->text_filter_key_press = fl_mock_view_keyboard_text_filter_key_press;
- iface->get_messenger = fl_mock_view_keyboard_get_messenger;
- iface->redispatch_event = fl_mock_view_keyboard_redispatch_event;
- iface->lookup_key = fl_mock_view_keyboard_lookup_key;
iface->get_keyboard_state = fl_mock_view_keyboard_get_keyboard_state;
}
-static FlMockViewDelegate* fl_mock_view_delegate_new() {
- FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(
- g_object_new(fl_mock_view_delegate_get_type(), nullptr));
+static FlMockKeyboardHandlerDelegate* fl_mock_keyboard_handler_delegate_new() {
+ FlMockKeyboardHandlerDelegate* self = FL_MOCK_KEYBOARD_HANDLER_DELEGATE(
+ g_object_new(fl_mock_keyboard_handler_delegate_get_type(), nullptr));
// Added to stop compiler complaining about an unused function.
- FL_IS_MOCK_VIEW_DELEGATE(self);
-
- FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(self);
- priv->messenger = fl_mock_key_binary_messenger_new();
+ FL_IS_MOCK_KEYBOARD_HANDLER_DELEGATE(self);
return self;
}
-static void fl_mock_view_set_embedder_handler(FlMockViewDelegate* self,
- EmbedderCallHandler handler) {
- FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(self);
- priv->embedder_handler = std::move(handler);
-}
-
-static void fl_mock_view_set_text_filter_result(FlMockViewDelegate* self,
- bool result) {
- FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(self);
- priv->text_filter_result = result;
-}
-
-static void fl_mock_view_set_redispatch_handler(FlMockViewDelegate* self,
- RedispatchHandler handler) {
- FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(self);
- priv->redispatch_handler = std::move(handler);
-}
-
-static void fl_mock_view_set_layout(FlMockViewDelegate* self,
- const MockLayoutData* layout) {
- FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(self);
- priv->layout_data = layout;
-}
-
-/***** End FlMockViewDelegate *****/
-
-class KeyboardTester {
- public:
- KeyboardTester() {
- ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
-
- view_ = fl_mock_view_delegate_new();
- respondToEmbedderCallsWith(false);
- respondToChannelCallsWith(false);
- respondToTextInputWith(false);
- setLayout(kLayoutUs);
-
- handler_ =
- fl_keyboard_handler_new(messenger, FL_KEYBOARD_VIEW_DELEGATE(view_));
- }
-
- ~KeyboardTester() {
- g_clear_object(&view_);
- g_clear_object(&handler_);
- g_clear_pointer(&redispatched_events_, g_ptr_array_unref);
- }
-
- FlKeyboardHandler* handler() { return handler_; }
-
- // Block until all GdkMainLoop messages are processed, which is basically
- // used only for channel messages.
- void flushChannelMessages() {
- GMainLoop* loop = g_main_loop_new(nullptr, 0);
- g_idle_add(_flushChannelMessagesCb, loop);
- g_main_loop_run(loop);
- }
-
- // Dispatch each of the given events, expect their results to be false
- // (unhandled), and clear the event array.
- //
- // Returns the number of events redispatched. If any result is unexpected
- // (handled), return a minus number `-x` instead, where `x` is the index of
- // the first unexpected redispatch.
- int redispatchEventsAndClear(GPtrArray* events) {
- guint event_count = events->len;
- int first_error = -1;
- during_redispatch_ = true;
- for (guint event_id = 0; event_id < event_count; event_id += 1) {
- FlKeyEvent* event = FL_KEY_EVENT(g_ptr_array_index(events, event_id));
- bool handled = fl_keyboard_handler_handle_event(handler_, event);
- EXPECT_FALSE(handled);
- if (handled) {
- first_error = first_error == -1 ? event_id : first_error;
- }
- }
- during_redispatch_ = false;
- g_ptr_array_set_size(events, 0);
- return first_error < 0 ? event_count : -first_error;
- }
-
- void respondToEmbedderCallsWith(bool response) {
- fl_mock_view_set_embedder_handler(
- view_, [response, this](const FlutterKeyEvent* event,
- const AsyncKeyCallback& callback) {
- EXPECT_FALSE(during_redispatch_);
- callback(response);
- });
- }
-
- void recordEmbedderCallsTo(std::vector<CallRecord>& storage) {
- fl_mock_view_set_embedder_handler(
- view_, [&storage, this](const FlutterKeyEvent* event,
- AsyncKeyCallback callback) {
- EXPECT_FALSE(during_redispatch_);
- auto new_event = std::make_unique<FlutterKeyEvent>(*event);
- char* new_event_character = cloneString(event->character);
- new_event->character = new_event_character;
- storage.push_back(CallRecord{
- .type = CallRecord::kKeyCallEmbedder,
- .callback = std::move(callback),
- .event = std::move(new_event),
- .event_character = std::unique_ptr<char[]>(new_event_character),
- });
- });
- }
-
- void respondToEmbedderCallsWithAndRecordsTo(
- bool response,
- std::vector<CallRecord>& storage) {
- fl_mock_view_set_embedder_handler(
- view_, [&storage, response, this](const FlutterKeyEvent* event,
- const AsyncKeyCallback& callback) {
- EXPECT_FALSE(during_redispatch_);
- auto new_event = std::make_unique<FlutterKeyEvent>(*event);
- char* new_event_character = cloneString(event->character);
- new_event->character = new_event_character;
- storage.push_back(CallRecord{
- .type = CallRecord::kKeyCallEmbedder,
- .event = std::move(new_event),
- .event_character = std::unique_ptr<char[]>(new_event_character),
- });
- callback(response);
- });
- }
-
- void respondToChannelCallsWith(bool response) {
- FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(view_);
-
- fl_mock_key_binary_messenger_set_callback_handler(
- priv->messenger, [response, this](const AsyncKeyCallback& callback) {
- EXPECT_FALSE(during_redispatch_);
- callback(response);
- });
- }
-
- void recordChannelCallsTo(std::vector<CallRecord>& storage) {
- FlMockViewDelegatePrivate* priv = FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(view_);
-
- fl_mock_key_binary_messenger_set_callback_handler(
- priv->messenger, [&storage, this](AsyncKeyCallback callback) {
- EXPECT_FALSE(during_redispatch_);
- storage.push_back(CallRecord{
- .type = CallRecord::kKeyCallChannel,
- .callback = std::move(callback),
- });
- });
- }
-
- void respondToTextInputWith(bool response) {
- fl_mock_view_set_text_filter_result(view_, response);
- }
-
- void recordRedispatchedEventsTo(GPtrArray* storage) {
- redispatched_events_ = g_ptr_array_ref(storage);
- fl_mock_view_set_redispatch_handler(view_, [this](FlKeyEvent* key) {
- g_ptr_array_add(redispatched_events_, g_object_ref(key));
- });
- }
-
- void setLayout(const MockLayoutData& layout) {
- fl_mock_view_set_layout(view_, &layout);
- if (handler_ != nullptr) {
- fl_keyboard_handler_notify_layout_changed(handler_);
- }
- }
-
- private:
- FlMockViewDelegate* view_;
- FlKeyboardHandler* handler_ = nullptr;
- GPtrArray* redispatched_events_ = nullptr;
- bool during_redispatch_ = false;
-
- static gboolean _flushChannelMessagesCb(gpointer data) {
- g_autoptr(GMainLoop) loop = reinterpret_cast<GMainLoop*>(data);
- g_main_loop_quit(loop);
- return FALSE;
- }
-};
-
-// Make sure that the keyboard can be disposed without crashes when there are
-// unresolved pending events.
-TEST(FlKeyboardHandlerTest, DisposeWithUnresolvedPends) {
- KeyboardTester tester;
- std::vector<CallRecord> call_records;
-
- // Record calls so that they aren't responded.
- tester.recordEmbedderCallsTo(call_records);
- g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
- 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
- fl_keyboard_handler_handle_event(tester.handler(), event1);
-
- tester.respondToEmbedderCallsWith(true);
- g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
- 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
- fl_keyboard_handler_handle_event(tester.handler(), event2);
-
- tester.flushChannelMessages();
-
- // Passes if the cleanup does not crash.
-}
-
-TEST(FlKeyboardHandlerTest, SingleDelegateWithAsyncResponds) {
- KeyboardTester tester;
- std::vector<CallRecord> call_records;
- g_autoptr(GPtrArray) redispatched =
- g_ptr_array_new_with_free_func(g_object_unref);
-
- gboolean handler_handled = false;
-
- /// Test 1: One event that is handled by the framework
- tester.recordEmbedderCallsTo(call_records);
- tester.recordRedispatchedEventsTo(redispatched);
-
- // Dispatch a key event
- g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
- 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
- handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event1);
- tester.flushChannelMessages();
- EXPECT_EQ(handler_handled, true);
- EXPECT_EQ(redispatched->len, 0u);
- EXPECT_EQ(call_records.size(), 1u);
- EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, kPhysicalKeyA,
- kLogicalKeyA, "a", false);
-
- call_records[0].callback(true);
- tester.flushChannelMessages();
- EXPECT_EQ(redispatched->len, 0u);
- EXPECT_TRUE(fl_keyboard_handler_is_state_clear(tester.handler()));
- call_records.clear();
-
- /// Test 2: Two events that are unhandled by the framework
- g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
- 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
- handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event2);
- tester.flushChannelMessages();
- EXPECT_EQ(handler_handled, true);
- EXPECT_EQ(redispatched->len, 0u);
- EXPECT_EQ(call_records.size(), 1u);
- EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeUp, kPhysicalKeyA,
- kLogicalKeyA, nullptr, false);
-
- // Dispatch another key event
- g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
- 0, TRUE, kKeyCodeKeyB, GDK_KEY_b, static_cast<GdkModifierType>(0), 0);
- handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event3);
- tester.flushChannelMessages();
- EXPECT_EQ(handler_handled, true);
- EXPECT_EQ(redispatched->len, 0u);
- EXPECT_EQ(call_records.size(), 2u);
- EXPECT_KEY_EVENT(call_records[1], kFlutterKeyEventTypeDown, kPhysicalKeyB,
- kLogicalKeyB, "b", false);
-
- // Resolve the second event first to test out-of-order response
- call_records[1].callback(false);
- EXPECT_EQ(redispatched->len, 1u);
- EXPECT_EQ(
- fl_key_event_get_keyval(FL_KEY_EVENT(g_ptr_array_index(redispatched, 0))),
- 0x62u);
- call_records[0].callback(false);
- tester.flushChannelMessages();
- EXPECT_EQ(redispatched->len, 2u);
- EXPECT_EQ(
- fl_key_event_get_keyval(FL_KEY_EVENT(g_ptr_array_index(redispatched, 1))),
- 0x61u);
-
- EXPECT_FALSE(fl_keyboard_handler_is_state_clear(tester.handler()));
- call_records.clear();
-
- // Resolve redispatches
- EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 2);
- tester.flushChannelMessages();
- EXPECT_EQ(call_records.size(), 0u);
- EXPECT_TRUE(fl_keyboard_handler_is_state_clear(tester.handler()));
-
- /// Test 3: Dispatch the same event again to ensure that prevention from
- /// redispatching only works once.
- g_autoptr(FlKeyEvent) event4 = fl_key_event_new(
- 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
- handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event4);
- tester.flushChannelMessages();
- EXPECT_EQ(handler_handled, true);
- EXPECT_EQ(redispatched->len, 0u);
- EXPECT_EQ(call_records.size(), 1u);
-
- call_records[0].callback(true);
- EXPECT_TRUE(fl_keyboard_handler_is_state_clear(tester.handler()));
-}
-
-TEST(FlKeyboardHandlerTest, SingleDelegateWithSyncResponds) {
- KeyboardTester tester;
- gboolean handler_handled = false;
- std::vector<CallRecord> call_records;
- g_autoptr(GPtrArray) redispatched =
- g_ptr_array_new_with_free_func(g_object_unref);
-
- /// Test 1: One event that is handled by the framework
- tester.respondToEmbedderCallsWithAndRecordsTo(true, call_records);
- tester.recordRedispatchedEventsTo(redispatched);
-
- // Dispatch a key event
- g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
- 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
- handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event1);
- tester.flushChannelMessages();
- EXPECT_EQ(handler_handled, true);
- EXPECT_EQ(call_records.size(), 1u);
- EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, kPhysicalKeyA,
- kLogicalKeyA, "a", false);
- EXPECT_EQ(redispatched->len, 0u);
- call_records.clear();
-
- EXPECT_TRUE(fl_keyboard_handler_is_state_clear(tester.handler()));
- g_ptr_array_set_size(redispatched, 0);
-
- /// Test 2: An event unhandled by the framework
- tester.respondToEmbedderCallsWithAndRecordsTo(false, call_records);
- g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
- 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
- handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event2);
- tester.flushChannelMessages();
- EXPECT_EQ(handler_handled, true);
- EXPECT_EQ(call_records.size(), 1u);
- EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeUp, kPhysicalKeyA,
- kLogicalKeyA, nullptr, false);
- EXPECT_EQ(redispatched->len, 1u);
- call_records.clear();
-
- EXPECT_FALSE(fl_keyboard_handler_is_state_clear(tester.handler()));
-
- EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
- EXPECT_EQ(call_records.size(), 0u);
-
- EXPECT_TRUE(fl_keyboard_handler_is_state_clear(tester.handler()));
-}
-
-TEST(FlKeyboardHandlerTest, WithTwoAsyncDelegates) {
- KeyboardTester tester;
- std::vector<CallRecord> call_records;
- g_autoptr(GPtrArray) redispatched =
- g_ptr_array_new_with_free_func(g_object_unref);
-
- gboolean handler_handled = false;
-
- tester.recordEmbedderCallsTo(call_records);
- tester.recordChannelCallsTo(call_records);
- tester.recordRedispatchedEventsTo(redispatched);
-
- /// Test 1: One delegate responds true, the other false
-
- g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
- 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
- handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event1);
-
- EXPECT_EQ(handler_handled, true);
- EXPECT_EQ(redispatched->len, 0u);
- EXPECT_EQ(call_records.size(), 2u);
-
- EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder);
- EXPECT_EQ(call_records[1].type, CallRecord::kKeyCallChannel);
-
- call_records[0].callback(true);
- call_records[1].callback(false);
- tester.flushChannelMessages();
- EXPECT_EQ(redispatched->len, 0u);
-
- EXPECT_TRUE(fl_keyboard_handler_is_state_clear(tester.handler()));
- call_records.clear();
-
- /// Test 2: All delegates respond false
- g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
- 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
- handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event2);
-
- EXPECT_EQ(handler_handled, true);
- EXPECT_EQ(redispatched->len, 0u);
- EXPECT_EQ(call_records.size(), 2u);
-
- EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder);
- EXPECT_EQ(call_records[1].type, CallRecord::kKeyCallChannel);
-
- call_records[0].callback(false);
- call_records[1].callback(false);
-
- call_records.clear();
-
- // Resolve redispatch
- tester.flushChannelMessages();
- EXPECT_EQ(redispatched->len, 1u);
- EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
- EXPECT_EQ(call_records.size(), 0u);
-
- EXPECT_TRUE(fl_keyboard_handler_is_state_clear(tester.handler()));
-}
-
-TEST(FlKeyboardHandlerTest, TextInputHandlerReturnsFalse) {
- KeyboardTester tester;
- g_autoptr(GPtrArray) redispatched =
- g_ptr_array_new_with_free_func(g_object_unref);
- gboolean handler_handled = false;
- tester.recordRedispatchedEventsTo(redispatched);
- tester.respondToTextInputWith(false);
-
- // Dispatch a key event.
- g_autoptr(FlKeyEvent) event = fl_key_event_new(
- 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
- handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event);
- tester.flushChannelMessages();
- EXPECT_EQ(handler_handled, true);
- // The event was redispatched because no one handles it.
- EXPECT_EQ(redispatched->len, 1u);
-
- // Resolve redispatched event.
- EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
-
- EXPECT_TRUE(fl_keyboard_handler_is_state_clear(tester.handler()));
-}
-
-TEST(FlKeyboardHandlerTest, TextInputHandlerReturnsTrue) {
- KeyboardTester tester;
- g_autoptr(GPtrArray) redispatched =
- g_ptr_array_new_with_free_func(g_object_unref);
- gboolean handler_handled = false;
- tester.recordRedispatchedEventsTo(redispatched);
- tester.respondToTextInputWith(true);
-
- // Dispatch a key event.
- g_autoptr(FlKeyEvent) event = fl_key_event_new(
- 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
- handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event);
- tester.flushChannelMessages();
- EXPECT_EQ(handler_handled, true);
- // The event was not redispatched because handler handles it.
- EXPECT_EQ(redispatched->len, 0u);
-
- EXPECT_TRUE(fl_keyboard_handler_is_state_clear(tester.handler()));
-}
-
-TEST(FlKeyboardHandlerTest, CorrectLogicalKeyForLayouts) {
- KeyboardTester tester;
-
- std::vector<CallRecord> call_records;
- tester.recordEmbedderCallsTo(call_records);
-
- auto sendTap = [&](guint8 keycode, guint keyval, guint8 group) {
- g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
- 0, TRUE, keycode, keyval, static_cast<GdkModifierType>(0), group);
- fl_keyboard_handler_handle_event(tester.handler(), event1);
- g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
- 0, FALSE, keycode, keyval, static_cast<GdkModifierType>(0), group);
- fl_keyboard_handler_handle_event(tester.handler(), event2);
- };
-
- /* US keyboard layout */
-
- sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
- VERIFY_DOWN(kLogicalKeyA, "a");
-
- sendTap(kKeyCodeKeyA, GDK_KEY_A, 0); // Shift-KeyA
- VERIFY_DOWN(kLogicalKeyA, "A");
-
- sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
- VERIFY_DOWN(kLogicalDigit1, "1");
-
- sendTap(kKeyCodeDigit1, GDK_KEY_exclam, 0); // Shift-Digit1
- VERIFY_DOWN(kLogicalDigit1, "!");
-
- sendTap(kKeyCodeMinus, GDK_KEY_minus, 0); // Minus
- VERIFY_DOWN(kLogicalMinus, "-");
-
- sendTap(kKeyCodeMinus, GDK_KEY_underscore, 0); // Shift-Minus
- VERIFY_DOWN(kLogicalUnderscore, "_");
-
- /* French keyboard layout, group 3, which is when the input method is showing
- * "Fr" */
-
- tester.setLayout(kLayoutFrench);
-
- sendTap(kKeyCodeKeyA, GDK_KEY_q, 3); // KeyA
- VERIFY_DOWN(kLogicalKeyQ, "q");
-
- sendTap(kKeyCodeKeyA, GDK_KEY_Q, 3); // Shift-KeyA
- VERIFY_DOWN(kLogicalKeyQ, "Q");
-
- sendTap(kKeyCodeSemicolon, GDK_KEY_m, 3); // ; but prints M
- VERIFY_DOWN(kLogicalKeyM, "m");
-
- sendTap(kKeyCodeKeyM, GDK_KEY_comma, 3); // M but prints ,
- VERIFY_DOWN(kLogicalComma, ",");
-
- sendTap(kKeyCodeDigit1, GDK_KEY_ampersand, 3); // Digit1
- VERIFY_DOWN(kLogicalDigit1, "&");
-
- sendTap(kKeyCodeDigit1, GDK_KEY_1, 3); // Shift-Digit1
- VERIFY_DOWN(kLogicalDigit1, "1");
-
- sendTap(kKeyCodeMinus, GDK_KEY_parenright, 3); // Minus
- VERIFY_DOWN(kLogicalParenthesisRight, ")");
-
- sendTap(kKeyCodeMinus, GDK_KEY_degree, 3); // Shift-Minus
- VERIFY_DOWN(static_cast<uint32_t>(L'°'), "°");
-
- /* French keyboard layout, group 0, which is pressing the "extra key for
- * triggering input method" key once after switching to French IME. */
-
- sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
- VERIFY_DOWN(kLogicalKeyA, "a");
-
- sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
- VERIFY_DOWN(kLogicalDigit1, "1");
-
- /* Russian keyboard layout, group 2 */
- tester.setLayout(kLayoutRussian);
-
- sendTap(kKeyCodeKeyA, GDK_KEY_Cyrillic_ef, 2); // KeyA
- VERIFY_DOWN(kLogicalKeyA, "ф");
-
- sendTap(kKeyCodeDigit1, GDK_KEY_1, 2); // Shift-Digit1
- VERIFY_DOWN(kLogicalDigit1, "1");
-
- sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_Cyrillic_ha, 2);
- VERIFY_DOWN(kLogicalBracketLeft, "х");
-
- /* Russian keyboard layout, group 0 */
- sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
- VERIFY_DOWN(kLogicalKeyA, "a");
-
- sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_bracketleft, 0);
- VERIFY_DOWN(kLogicalBracketLeft, "[");
-}
-
-TEST(FlKeyboardHandlerTest, SynthesizeModifiersIfNeeded) {
- KeyboardTester tester;
- std::vector<CallRecord> call_records;
- tester.recordEmbedderCallsTo(call_records);
-
- auto verifyModifierIsSynthesized = [&](GdkModifierType mask,
- uint64_t physical, uint64_t logical) {
- // Modifier is pressed.
- guint state = mask;
- fl_keyboard_handler_sync_modifier_if_needed(tester.handler(), state, 1000);
- EXPECT_EQ(call_records.size(), 1u);
- EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, physical,
- logical, NULL, true);
- // Modifier is released.
- state = state ^ mask;
- fl_keyboard_handler_sync_modifier_if_needed(tester.handler(), state, 1001);
- EXPECT_EQ(call_records.size(), 2u);
- EXPECT_KEY_EVENT(call_records[1], kFlutterKeyEventTypeUp, physical, logical,
- NULL, true);
- call_records.clear();
- };
-
- // No modifiers pressed.
- guint state = 0;
- fl_keyboard_handler_sync_modifier_if_needed(tester.handler(), state, 1000);
- EXPECT_EQ(call_records.size(), 0u);
- call_records.clear();
-
- // Press and release each modifier once.
- verifyModifierIsSynthesized(GDK_CONTROL_MASK, kPhysicalControlLeft,
- kLogicalControlLeft);
- verifyModifierIsSynthesized(GDK_META_MASK, kPhysicalMetaLeft,
- kLogicalMetaLeft);
- verifyModifierIsSynthesized(GDK_MOD1_MASK, kPhysicalAltLeft, kLogicalAltLeft);
- verifyModifierIsSynthesized(GDK_SHIFT_MASK, kPhysicalShiftLeft,
- kLogicalShiftLeft);
-}
-
-TEST(FlKeyboardHandlerTest, GetPressedState) {
- KeyboardTester tester;
- tester.respondToTextInputWith(true);
-
- // Dispatch a key event.
- g_autoptr(FlKeyEvent) event = fl_key_event_new(
- 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
- fl_keyboard_handler_handle_event(tester.handler(), event);
-
- GHashTable* pressedState =
- fl_keyboard_handler_get_pressed_state(tester.handler());
- EXPECT_EQ(g_hash_table_size(pressedState), 1u);
-
- gpointer physical_key =
- g_hash_table_lookup(pressedState, uint64_to_gpointer(kPhysicalKeyA));
- EXPECT_EQ(gpointer_to_uint64(physical_key), kLogicalKeyA);
-}
-
TEST(FlKeyboardHandlerTest, KeyboardChannelGetPressedState) {
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
g_autoptr(FlKeyboardHandler) handler = fl_keyboard_handler_new(
- messenger, FL_KEYBOARD_VIEW_DELEGATE(fl_mock_view_delegate_new()));
+ messenger,
+ FL_KEYBOARD_VIEW_DELEGATE(fl_mock_keyboard_handler_delegate_new()));
EXPECT_NE(handler, nullptr);
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
@@ -1034,193 +107,3 @@
messenger.ReceiveMessage(kKeyboardChannelName, message);
}
-
-// The following layout data is generated using DEBUG_PRINT_LAYOUT.
-
-const MockGroupLayoutData kLayoutUs0{{
- // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
- 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
- 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
- 0xffff, 0x0031, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
- 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
- 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
- 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
- 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
- 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
- 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
- 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
- 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
- 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
- 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
- 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
- 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
- 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
- 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
- 0xffff, 0xffff, 0x003c, 0x003e, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x64
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
- 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
-}};
-
-const MockGroupLayoutData kLayoutRussian0{
- // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
- 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
- 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
- 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
- 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
- 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
- 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
- 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
- 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
- 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
- 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
- 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
- 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
- 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
- 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
- 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
- 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
- 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
- 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
- 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
- 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
- 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
-};
-
-const MockGroupLayoutData kLayoutRussian2{{
- // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
- 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
- 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
- 0xffff, 0x0031, 0x0021, 0x0000, 0x0031, 0x0021, 0x0032, 0x0022, // 0x08
- 0x0033, 0x06b0, 0x0034, 0x003b, 0x0035, 0x0025, 0x0036, 0x003a, // 0x0c
- 0x0037, 0x003f, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
- 0x002d, 0x005f, 0x003d, 0x002b, 0x0071, 0x0051, 0x0000, 0x0000, // 0x14
- 0x06ca, 0x06ea, 0x06c3, 0x06e3, 0x06d5, 0x06f5, 0x06cb, 0x06eb, // 0x18
- 0x06c5, 0x06e5, 0x06ce, 0x06ee, 0x06c7, 0x06e7, 0x06db, 0x06fb, // 0x1c
- 0x06dd, 0x06fd, 0x06da, 0x06fa, 0x06c8, 0x06e8, 0x06df, 0x06ff, // 0x20
- 0x0061, 0x0041, 0x0041, 0x0000, 0x06c6, 0x06e6, 0x06d9, 0x06f9, // 0x24
- 0x06d7, 0x06f7, 0x06c1, 0x06e1, 0x06d0, 0x06f0, 0x06d2, 0x06f2, // 0x28
- 0x06cf, 0x06ef, 0x06cc, 0x06ec, 0x06c4, 0x06e4, 0x06d6, 0x06f6, // 0x2c
- 0x06dc, 0x06fc, 0x06a3, 0x06b3, 0x007c, 0x0000, 0x005c, 0x002f, // 0x30
- 0x06d1, 0x06f1, 0x06de, 0x06fe, 0x06d3, 0x06f3, 0x06cd, 0x06ed, // 0x34
- 0x06c9, 0x06e9, 0x06d4, 0x06f4, 0x06d8, 0x06f8, 0x06c2, 0x06e2, // 0x38
- 0x06c0, 0x06e0, 0x002e, 0x002c, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
- 0xffff, 0xffff, 0x003c, 0x003e, 0x002f, 0x007c, 0xffff, 0xffff, // 0x5c
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x64
- 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0x0000, // 0x68
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, // 0x78
- 0x00b1, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x7c
-}};
-
-const MockGroupLayoutData kLayoutFrench0 = {
- // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
- 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
- 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
- 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
- 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
- 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
- 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
- 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
- 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
- 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
- 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
- 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
- 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
- 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
- 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
- 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
- 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
- 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
- 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
- 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
- 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
- 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
-};
-
-const MockGroupLayoutData kLayoutFrench3 = {
- // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
- 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
- 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
- 0x0000, 0xffff, 0x0000, 0x0000, 0x0026, 0x0031, 0x00e9, 0x0032, // 0x08
- 0x0022, 0x0033, 0x0027, 0x0034, 0x0028, 0x0035, 0x002d, 0x0036, // 0x0c
- 0x00e8, 0x0037, 0x005f, 0x0038, 0x00e7, 0x0039, 0x00e0, 0x0030, // 0x10
- 0x0029, 0x00b0, 0x003d, 0x002b, 0x0000, 0x0000, 0x0061, 0x0041, // 0x14
- 0x0061, 0x0041, 0x007a, 0x005a, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
- 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
- 0x006f, 0x004f, 0x0070, 0x0050, 0xffff, 0xffff, 0x0024, 0x00a3, // 0x20
- 0x0041, 0x0000, 0x0000, 0x0000, 0x0071, 0x0051, 0x0073, 0x0053, // 0x24
- 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
- 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x006d, 0x004d, // 0x2c
- 0x00f9, 0x0025, 0x00b2, 0x007e, 0x0000, 0x0000, 0x002a, 0x00b5, // 0x30
- 0x0077, 0x0057, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
- 0x0062, 0x0042, 0x006e, 0x004e, 0x002c, 0x003f, 0x003b, 0x002e, // 0x38
- 0x003a, 0x002f, 0x0021, 0x00a7, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
- 0xffff, 0x003c, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
- 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
- 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
- 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, // 0x78
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x7c
-};
-
-const MockLayoutData kLayoutUs{&kLayoutUs0};
-const MockLayoutData kLayoutRussian{&kLayoutRussian0, nullptr,
- &kLayoutRussian2};
-const MockLayoutData kLayoutFrench{&kLayoutFrench0, nullptr, nullptr,
- &kLayoutFrench3};
-
-} // namespace
diff --git a/shell/platform/linux/fl_keyboard_manager.cc b/shell/platform/linux/fl_keyboard_manager.cc
new file mode 100644
index 0000000..3c0b8f1
--- /dev/null
+++ b/shell/platform/linux/fl_keyboard_manager.cc
@@ -0,0 +1,510 @@
+// 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/linux/fl_keyboard_manager.h"
+
+#include <array>
+#include <cinttypes>
+#include <memory>
+#include <string>
+
+#include "flutter/shell/platform/linux/fl_key_channel_responder.h"
+#include "flutter/shell/platform/linux/fl_key_embedder_responder.h"
+#include "flutter/shell/platform/linux/fl_keyboard_layout.h"
+#include "flutter/shell/platform/linux/fl_keyboard_pending_event.h"
+#include "flutter/shell/platform/linux/key_mapping.h"
+
+// Turn on this flag to print complete layout data when switching IMEs. The data
+// is used in unit tests.
+#define DEBUG_PRINT_LAYOUT
+
+/* Declarations of private classes */
+
+G_DECLARE_FINAL_TYPE(FlKeyboardManagerUserData,
+ fl_keyboard_manager_user_data,
+ FL,
+ KEYBOARD_MANAGER_USER_DATA,
+ GObject);
+
+/* End declarations */
+
+namespace {
+
+static bool is_eascii(uint16_t character) {
+ return character < 256;
+}
+
+#ifdef DEBUG_PRINT_LAYOUT
+// Prints layout entries that will be parsed by `MockLayoutData`.
+void debug_format_layout_data(std::string& debug_layout_data,
+ uint16_t keycode,
+ uint16_t clue1,
+ uint16_t clue2) {
+ if (keycode % 4 == 0) {
+ debug_layout_data.append(" ");
+ }
+
+ constexpr int kBufferSize = 30;
+ char buffer[kBufferSize];
+ buffer[0] = 0;
+ buffer[kBufferSize - 1] = 0;
+
+ snprintf(buffer, kBufferSize, "0x%04x, 0x%04x, ", clue1, clue2);
+ debug_layout_data.append(buffer);
+
+ if (keycode % 4 == 3) {
+ snprintf(buffer, kBufferSize, " // 0x%02x", keycode);
+ debug_layout_data.append(buffer);
+ }
+}
+#endif
+
+} // namespace
+
+/* Define FlKeyboardManagerUserData */
+
+/**
+ * FlKeyboardManagerUserData:
+ * The user_data used when #FlKeyboardManager sends event to
+ * responders.
+ */
+
+struct _FlKeyboardManagerUserData {
+ GObject parent_instance;
+
+ // The owner manager.
+ GWeakRef manager;
+ uint64_t sequence_id;
+};
+
+G_DEFINE_TYPE(FlKeyboardManagerUserData,
+ fl_keyboard_manager_user_data,
+ G_TYPE_OBJECT)
+
+static void fl_keyboard_manager_user_data_dispose(GObject* object) {
+ g_return_if_fail(FL_IS_KEYBOARD_MANAGER_USER_DATA(object));
+ FlKeyboardManagerUserData* self = FL_KEYBOARD_MANAGER_USER_DATA(object);
+
+ g_weak_ref_clear(&self->manager);
+
+ G_OBJECT_CLASS(fl_keyboard_manager_user_data_parent_class)->dispose(object);
+}
+
+static void fl_keyboard_manager_user_data_class_init(
+ FlKeyboardManagerUserDataClass* klass) {
+ G_OBJECT_CLASS(klass)->dispose = fl_keyboard_manager_user_data_dispose;
+}
+
+static void fl_keyboard_manager_user_data_init(
+ FlKeyboardManagerUserData* self) {}
+
+// Creates a new FlKeyboardManagerUserData private class with all information.
+static FlKeyboardManagerUserData* fl_keyboard_manager_user_data_new(
+ FlKeyboardManager* manager,
+ uint64_t sequence_id) {
+ FlKeyboardManagerUserData* self = FL_KEYBOARD_MANAGER_USER_DATA(
+ g_object_new(fl_keyboard_manager_user_data_get_type(), nullptr));
+
+ g_weak_ref_init(&self->manager, manager);
+ self->sequence_id = sequence_id;
+ return self;
+}
+
+/* Define FlKeyboardManager */
+
+struct _FlKeyboardManager {
+ GObject parent_instance;
+
+ GWeakRef view_delegate;
+
+ // An array of #FlKeyResponder. Elements are added with
+ // #fl_keyboard_manager_add_responder immediately after initialization and are
+ // automatically released on dispose.
+ GPtrArray* responder_list;
+
+ // An array of #FlKeyboardPendingEvent.
+ //
+ // Its elements are *not* unreferenced when removed. When FlKeyboardManager is
+ // disposed, this array will be set with a free_func so that the elements are
+ // unreferenced when removed.
+ GPtrArray* pending_responds;
+
+ // An array of #FlKeyboardPendingEvent.
+ //
+ // Its elements are unreferenced when removed.
+ GPtrArray* pending_redispatches;
+
+ // The last sequence ID used. Increased by 1 by every use.
+ uint64_t last_sequence_id;
+
+ // Record the derived layout.
+ //
+ // It is cleared when the platform reports a layout switch. Each entry,
+ // which corresponds to a group, is only initialized on the arrival of the
+ // first event for that group that has a goal keycode.
+ FlKeyboardLayout* derived_layout;
+
+ // A static map from keycodes to all layout goals.
+ //
+ // It is set up when the manager is initialized and is not changed ever after.
+ std::unique_ptr<std::map<uint16_t, const LayoutGoal*>> keycode_to_goals;
+
+ // A static map from logical keys to all mandatory layout goals.
+ //
+ // It is set up when the manager is initialized and is not changed ever after.
+ std::unique_ptr<std::map<uint64_t, const LayoutGoal*>>
+ logical_to_mandatory_goals;
+};
+
+G_DEFINE_TYPE(FlKeyboardManager, fl_keyboard_manager, G_TYPE_OBJECT);
+
+// This is an exact copy of g_ptr_array_find_with_equal_func. Somehow CI
+// reports that can not find symbol g_ptr_array_find_with_equal_func, despite
+// the fact that it runs well locally.
+static gboolean g_ptr_array_find_with_equal_func1(GPtrArray* haystack,
+ gconstpointer needle,
+ GEqualFunc equal_func,
+ guint* index_) {
+ guint i;
+ g_return_val_if_fail(haystack != NULL, FALSE);
+ if (equal_func == NULL) {
+ equal_func = g_direct_equal;
+ }
+ for (i = 0; i < haystack->len; i++) {
+ if (equal_func(g_ptr_array_index(haystack, i), needle)) {
+ if (index_ != NULL) {
+ *index_ = i;
+ }
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+// Compare a #FlKeyboardPendingEvent with the given sequence_id.
+static gboolean compare_pending_by_sequence_id(gconstpointer a,
+ gconstpointer b) {
+ FlKeyboardPendingEvent* pending =
+ FL_KEYBOARD_PENDING_EVENT(const_cast<gpointer>(a));
+ uint64_t sequence_id = *reinterpret_cast<const uint64_t*>(b);
+ return fl_keyboard_pending_event_get_sequence_id(pending) == sequence_id;
+}
+
+// Compare a #FlKeyboardPendingEvent with the given hash.
+static gboolean compare_pending_by_hash(gconstpointer a, gconstpointer b) {
+ FlKeyboardPendingEvent* pending =
+ FL_KEYBOARD_PENDING_EVENT(const_cast<gpointer>(a));
+ uint64_t hash = *reinterpret_cast<const uint64_t*>(b);
+ return fl_keyboard_pending_event_get_hash(pending) == hash;
+}
+
+// Try to remove a pending event from `pending_redispatches` with the target
+// hash.
+//
+// Returns true if the event is found and removed.
+static bool fl_keyboard_manager_remove_redispatched(FlKeyboardManager* self,
+ uint64_t hash) {
+ guint result_index;
+ gboolean found = g_ptr_array_find_with_equal_func1(
+ self->pending_redispatches, static_cast<const uint64_t*>(&hash),
+ compare_pending_by_hash, &result_index);
+ if (found) {
+ // The removed object is freed due to `pending_redispatches`'s free_func.
+ g_ptr_array_remove_index_fast(self->pending_redispatches, result_index);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+// The callback used by a responder after the event was dispatched.
+static void responder_handle_event_callback(bool handled,
+ gpointer user_data_ptr) {
+ g_return_if_fail(FL_IS_KEYBOARD_MANAGER_USER_DATA(user_data_ptr));
+ FlKeyboardManagerUserData* user_data =
+ FL_KEYBOARD_MANAGER_USER_DATA(user_data_ptr);
+
+ g_autoptr(FlKeyboardManager) self =
+ FL_KEYBOARD_MANAGER(g_weak_ref_get(&user_data->manager));
+ if (self == nullptr) {
+ return;
+ }
+
+ g_autoptr(FlKeyboardViewDelegate) view_delegate =
+ FL_KEYBOARD_VIEW_DELEGATE(g_weak_ref_get(&self->view_delegate));
+ if (view_delegate == nullptr) {
+ return;
+ }
+
+ guint result_index = -1;
+ gboolean found = g_ptr_array_find_with_equal_func1(
+ self->pending_responds, &user_data->sequence_id,
+ compare_pending_by_sequence_id, &result_index);
+ g_return_if_fail(found);
+ FlKeyboardPendingEvent* pending = FL_KEYBOARD_PENDING_EVENT(
+ g_ptr_array_index(self->pending_responds, result_index));
+ g_return_if_fail(pending != nullptr);
+ fl_keyboard_pending_event_mark_replied(pending, handled);
+ // All responders have replied.
+ if (fl_keyboard_pending_event_is_complete(pending)) {
+ g_object_unref(user_data_ptr);
+ gpointer removed =
+ g_ptr_array_remove_index_fast(self->pending_responds, result_index);
+ g_return_if_fail(removed == pending);
+ bool should_redispatch =
+ !fl_keyboard_pending_event_get_any_handled(pending) &&
+ !fl_keyboard_view_delegate_text_filter_key_press(
+ view_delegate, fl_keyboard_pending_event_get_event(pending));
+ if (should_redispatch) {
+ g_ptr_array_add(self->pending_redispatches, pending);
+ fl_keyboard_view_delegate_redispatch_event(
+ view_delegate,
+ FL_KEY_EVENT(fl_keyboard_pending_event_get_event(pending)));
+ } else {
+ g_object_unref(pending);
+ }
+ }
+}
+
+static uint16_t convert_key_to_char(FlKeyboardViewDelegate* view_delegate,
+ guint keycode,
+ gint group,
+ gint level) {
+ GdkKeymapKey key = {keycode, group, level};
+ constexpr int kBmpMax = 0xD7FF;
+ guint origin = fl_keyboard_view_delegate_lookup_key(view_delegate, &key);
+ return origin < kBmpMax ? origin : 0xFFFF;
+}
+
+// Make sure that Flutter has derived the layout for the group of the event,
+// if the event contains a goal keycode.
+static void guarantee_layout(FlKeyboardManager* self, FlKeyEvent* event) {
+ g_autoptr(FlKeyboardViewDelegate) view_delegate =
+ FL_KEYBOARD_VIEW_DELEGATE(g_weak_ref_get(&self->view_delegate));
+ if (view_delegate == nullptr) {
+ return;
+ }
+
+ guint8 group = fl_key_event_get_group(event);
+ if (fl_keyboard_layout_has_group(self->derived_layout, group)) {
+ return;
+ }
+ if (self->keycode_to_goals->find(fl_key_event_get_keycode(event)) ==
+ self->keycode_to_goals->end()) {
+ return;
+ }
+
+ // Clone all mandatory goals. Each goal is removed from this cloned map when
+ // fulfilled, and the remaining ones will be assigned to a default position.
+ std::map<uint64_t, const LayoutGoal*> remaining_mandatory_goals =
+ *self->logical_to_mandatory_goals;
+
+#ifdef DEBUG_PRINT_LAYOUT
+ std::string debug_layout_data;
+ for (uint16_t keycode = 0; keycode < 128; keycode += 1) {
+ std::vector<uint16_t> this_key_clues = {
+ convert_key_to_char(view_delegate, keycode, group, 0),
+ convert_key_to_char(view_delegate, keycode, group, 1), // Shift
+ };
+ debug_format_layout_data(debug_layout_data, keycode, this_key_clues[0],
+ this_key_clues[1]);
+ }
+#endif
+
+ // It's important to only traverse layout goals instead of all keycodes.
+ // Some key codes outside of the standard keyboard also gives alpha-numeric
+ // letters, and will therefore take over mandatory goals from standard
+ // keyboard keys if they come first. Example: French keyboard digit 1.
+ for (const LayoutGoal& keycode_goal : layout_goals) {
+ uint16_t keycode = keycode_goal.keycode;
+ std::vector<uint16_t> this_key_clues = {
+ convert_key_to_char(view_delegate, keycode, group, 0),
+ convert_key_to_char(view_delegate, keycode, group, 1), // Shift
+ };
+
+ // The logical key should be the first available clue from below:
+ //
+ // - Mandatory goal, if it matches any clue. This ensures that all alnum
+ // keys can be found somewhere.
+ // - US layout, if neither clue of the key is EASCII. This ensures that
+ // there are no non-latin logical keys.
+ // - A value derived on the fly from keycode & keyval.
+ for (uint16_t clue : this_key_clues) {
+ auto matching_goal = remaining_mandatory_goals.find(clue);
+ if (matching_goal != remaining_mandatory_goals.end()) {
+ // Found a key that produces a mandatory char. Use it.
+ g_return_if_fail(fl_keyboard_layout_get_logical_key(
+ self->derived_layout, group, keycode) == 0);
+ fl_keyboard_layout_set_logical_key(self->derived_layout, group, keycode,
+ clue);
+ remaining_mandatory_goals.erase(matching_goal);
+ break;
+ }
+ }
+ bool has_any_eascii =
+ is_eascii(this_key_clues[0]) || is_eascii(this_key_clues[1]);
+ // See if any produced char meets the requirement as a logical key.
+ if (fl_keyboard_layout_get_logical_key(self->derived_layout, group,
+ keycode) == 0 &&
+ !has_any_eascii) {
+ auto found_us_layout = self->keycode_to_goals->find(keycode);
+ if (found_us_layout != self->keycode_to_goals->end()) {
+ fl_keyboard_layout_set_logical_key(
+ self->derived_layout, group, keycode,
+ found_us_layout->second->logical_key);
+ }
+ }
+ }
+
+ // Ensure all mandatory goals are assigned.
+ for (const auto mandatory_goal_iter : remaining_mandatory_goals) {
+ const LayoutGoal* goal = mandatory_goal_iter.second;
+ fl_keyboard_layout_set_logical_key(self->derived_layout, group,
+ goal->keycode, goal->logical_key);
+ }
+}
+
+static void fl_keyboard_manager_dispose(GObject* object) {
+ FlKeyboardManager* self = FL_KEYBOARD_MANAGER(object);
+
+ g_weak_ref_clear(&self->view_delegate);
+
+ self->keycode_to_goals.reset();
+ self->logical_to_mandatory_goals.reset();
+
+ g_ptr_array_free(self->responder_list, TRUE);
+ g_ptr_array_set_free_func(self->pending_responds, g_object_unref);
+ g_ptr_array_free(self->pending_responds, TRUE);
+ g_ptr_array_free(self->pending_redispatches, TRUE);
+ g_clear_object(&self->derived_layout);
+
+ G_OBJECT_CLASS(fl_keyboard_manager_parent_class)->dispose(object);
+}
+
+static void fl_keyboard_manager_class_init(FlKeyboardManagerClass* klass) {
+ G_OBJECT_CLASS(klass)->dispose = fl_keyboard_manager_dispose;
+}
+
+static void fl_keyboard_manager_init(FlKeyboardManager* self) {
+ self->derived_layout = fl_keyboard_layout_new();
+
+ self->keycode_to_goals =
+ std::make_unique<std::map<uint16_t, const LayoutGoal*>>();
+ self->logical_to_mandatory_goals =
+ std::make_unique<std::map<uint64_t, const LayoutGoal*>>();
+ for (const LayoutGoal& goal : layout_goals) {
+ (*self->keycode_to_goals)[goal.keycode] = &goal;
+ if (goal.mandatory) {
+ (*self->logical_to_mandatory_goals)[goal.logical_key] = &goal;
+ }
+ }
+
+ self->responder_list = g_ptr_array_new_with_free_func(g_object_unref);
+
+ self->pending_responds = g_ptr_array_new();
+ self->pending_redispatches = g_ptr_array_new_with_free_func(g_object_unref);
+
+ self->last_sequence_id = 1;
+}
+
+FlKeyboardManager* fl_keyboard_manager_new(
+ FlBinaryMessenger* messenger,
+ FlKeyboardViewDelegate* view_delegate) {
+ g_return_val_if_fail(FL_IS_KEYBOARD_VIEW_DELEGATE(view_delegate), nullptr);
+
+ FlKeyboardManager* self = FL_KEYBOARD_MANAGER(
+ g_object_new(fl_keyboard_manager_get_type(), nullptr));
+
+ g_weak_ref_init(&self->view_delegate, view_delegate);
+
+ // The embedder responder must be added before the channel responder.
+ g_ptr_array_add(
+ self->responder_list,
+ FL_KEY_RESPONDER(fl_key_embedder_responder_new(
+ [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
+ void* callback_user_data, void* send_key_event_user_data) {
+ FlKeyboardManager* self =
+ FL_KEYBOARD_MANAGER(send_key_event_user_data);
+ g_autoptr(FlKeyboardViewDelegate) view_delegate =
+ FL_KEYBOARD_VIEW_DELEGATE(g_weak_ref_get(&self->view_delegate));
+ if (view_delegate == nullptr) {
+ return;
+ }
+ fl_keyboard_view_delegate_send_key_event(
+ view_delegate, event, callback, callback_user_data);
+ },
+ self)));
+ g_ptr_array_add(self->responder_list,
+ FL_KEY_RESPONDER(fl_key_channel_responder_new(messenger)));
+
+ return self;
+}
+
+gboolean fl_keyboard_manager_handle_event(FlKeyboardManager* self,
+ FlKeyEvent* event) {
+ g_return_val_if_fail(FL_IS_KEYBOARD_MANAGER(self), FALSE);
+ g_return_val_if_fail(event != nullptr, FALSE);
+
+ guarantee_layout(self, event);
+
+ uint64_t incoming_hash = fl_key_event_hash(event);
+ if (fl_keyboard_manager_remove_redispatched(self, incoming_hash)) {
+ return FALSE;
+ }
+
+ FlKeyboardPendingEvent* pending = fl_keyboard_pending_event_new(
+ event, ++self->last_sequence_id, self->responder_list->len);
+
+ g_ptr_array_add(self->pending_responds, pending);
+ FlKeyboardManagerUserData* user_data = fl_keyboard_manager_user_data_new(
+ self, fl_keyboard_pending_event_get_sequence_id(pending));
+ uint64_t specified_logical_key = fl_keyboard_layout_get_logical_key(
+ self->derived_layout, fl_key_event_get_group(event),
+ fl_key_event_get_keycode(event));
+ for (guint i = 0; i < self->responder_list->len; i++) {
+ FlKeyResponder* responder =
+ FL_KEY_RESPONDER(g_ptr_array_index(self->responder_list, i));
+ fl_key_responder_handle_event(responder, event,
+ responder_handle_event_callback, user_data,
+ specified_logical_key);
+ }
+
+ return TRUE;
+}
+
+gboolean fl_keyboard_manager_is_state_clear(FlKeyboardManager* self) {
+ g_return_val_if_fail(FL_IS_KEYBOARD_MANAGER(self), FALSE);
+ return self->pending_responds->len == 0 &&
+ self->pending_redispatches->len == 0;
+}
+
+void fl_keyboard_manager_sync_modifier_if_needed(FlKeyboardManager* self,
+ guint state,
+ double event_time) {
+ g_return_if_fail(FL_IS_KEYBOARD_MANAGER(self));
+
+ // The embedder responder is the first element in
+ // FlKeyboardManager.responder_list.
+ FlKeyEmbedderResponder* responder =
+ FL_KEY_EMBEDDER_RESPONDER(g_ptr_array_index(self->responder_list, 0));
+ fl_key_embedder_responder_sync_modifiers_if_needed(responder, state,
+ event_time);
+}
+
+GHashTable* fl_keyboard_manager_get_pressed_state(FlKeyboardManager* self) {
+ g_return_val_if_fail(FL_IS_KEYBOARD_MANAGER(self), nullptr);
+
+ // The embedder responder is the first element in
+ // FlKeyboardManager.responder_list.
+ FlKeyEmbedderResponder* responder =
+ FL_KEY_EMBEDDER_RESPONDER(g_ptr_array_index(self->responder_list, 0));
+ return fl_key_embedder_responder_get_pressed_state(responder);
+}
+
+void fl_keyboard_manager_notify_layout_changed(FlKeyboardManager* self) {
+ g_return_if_fail(FL_IS_KEYBOARD_MANAGER(self));
+ g_clear_object(&self->derived_layout);
+ self->derived_layout = fl_keyboard_layout_new();
+}
diff --git a/shell/platform/linux/fl_keyboard_manager.h b/shell/platform/linux/fl_keyboard_manager.h
new file mode 100644
index 0000000..383c7f9
--- /dev/null
+++ b/shell/platform/linux/fl_keyboard_manager.h
@@ -0,0 +1,108 @@
+// 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_LINUX_FL_KEYBOARD_MANAGER_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_KEYBOARD_MANAGER_H_
+
+#include <gdk/gdk.h>
+
+#include "flutter/shell/platform/linux/fl_keyboard_view_delegate.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"
+
+G_BEGIN_DECLS
+
+#define FL_TYPE_KEYBOARD_MANAGER fl_keyboard_manager_get_type()
+G_DECLARE_FINAL_TYPE(FlKeyboardManager,
+ fl_keyboard_manager,
+ FL,
+ KEYBOARD_MANAGER,
+ GObject);
+
+/**
+ * FlKeyboardManager:
+ *
+ * Processes keyboard events and cooperate with `TextInputManager`.
+ *
+ * A keyboard event goes through a few sections, each can choose to handle the
+ * event, and only unhandled events can move to the next section:
+ *
+ * - Keyboard: Dispatch to the embedder responder and the channel responder
+ * simultaneously. After both responders have responded (asynchronously), the
+ * event is considered handled if either responder handles it.
+ * - Text input: Events are sent to IM filter (usually owned by
+ * `TextInputManager`) and are handled synchronously.
+ * - Redispatching: Events are inserted back to the system for redispatching.
+ */
+
+/**
+ * fl_keyboard_manager_new:
+ * @messenger: an #FlBinaryMessenger.
+ * @view_delegate: An interface that the manager requires to communicate with
+ * the platform. Usually implemented by FlView.
+ *
+ * Create a new #FlKeyboardManager.
+ *
+ * Returns: a new #FlKeyboardManager.
+ */
+FlKeyboardManager* fl_keyboard_manager_new(
+ FlBinaryMessenger* messenger,
+ FlKeyboardViewDelegate* view_delegate);
+
+/**
+ * fl_keyboard_manager_handle_event:
+ * @manager: the #FlKeyboardManager self.
+ * @event: the event to be dispatched. It is usually a wrap of a GdkEventKey.
+ * This event will be managed and released by #FlKeyboardManager.
+ *
+ * Make the manager process a system key event. This might eventually send
+ * messages to the framework, trigger text input effects, or redispatch the
+ * event back to the system.
+ */
+gboolean fl_keyboard_manager_handle_event(FlKeyboardManager* manager,
+ FlKeyEvent* event);
+
+/**
+ * fl_keyboard_manager_is_state_clear:
+ * @manager: the #FlKeyboardManager self.
+ *
+ * A debug-only method that queries whether the manager's various states are
+ * cleared, i.e. no pending events for redispatching or for responding.
+ *
+ * Returns: true if the manager's various states are cleared.
+ */
+gboolean fl_keyboard_manager_is_state_clear(FlKeyboardManager* manager);
+
+/**
+ * fl_keyboard_manager_sync_modifier_if_needed:
+ * @manager: the #FlKeyboardManager self.
+ * @state: the state of the modifiers mask.
+ * @event_time: the time attribute of the incoming GDK event.
+ *
+ * If needed, synthesize modifier keys up and down event by comparing their
+ * current pressing states with the given modifiers mask.
+ */
+void fl_keyboard_manager_sync_modifier_if_needed(FlKeyboardManager* manager,
+ guint state,
+ double event_time);
+
+/**
+ * fl_keyboard_manager_get_pressed_state:
+ * @manager: the #FlKeyboardManager self.
+ *
+ * Returns the keyboard pressed state. The hash table contains one entry per
+ * pressed keys, mapping from the logical key to the physical key.*
+ */
+GHashTable* fl_keyboard_manager_get_pressed_state(FlKeyboardManager* manager);
+
+/**
+ * fl_keyboard_manager_notify_layout_changed:
+ * @manager: the #FlKeyboardManager self.
+ *
+ * Notify the manager the keyboard layout has changed.
+ */
+void fl_keyboard_manager_notify_layout_changed(FlKeyboardManager* manager);
+
+G_END_DECLS
+
+#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_KEYBOARD_MANAGER_H_
diff --git a/shell/platform/linux/fl_keyboard_manager_test.cc b/shell/platform/linux/fl_keyboard_manager_test.cc
new file mode 100644
index 0000000..d655df3
--- /dev/null
+++ b/shell/platform/linux/fl_keyboard_manager_test.cc
@@ -0,0 +1,1139 @@
+// 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/linux/fl_keyboard_manager.h"
+
+#include <cstring>
+#include <vector>
+
+#include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
+#include "flutter/shell/platform/linux/fl_binary_messenger_private.h"
+#include "flutter/shell/platform/linux/fl_method_codec_private.h"
+#include "flutter/shell/platform/linux/key_mapping.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
+#include "flutter/shell/platform/linux/testing/fl_test.h"
+#include "flutter/shell/platform/linux/testing/mock_text_input_handler.h"
+#include "flutter/testing/testing.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+// Define compound `expect` in macros. If they were defined in functions, the
+// stacktrace wouldn't print where the function is called in the unit tests.
+
+#define EXPECT_KEY_EVENT(RECORD, TYPE, PHYSICAL, LOGICAL, CHAR, SYNTHESIZED) \
+ EXPECT_EQ((RECORD).type, CallRecord::kKeyCallEmbedder); \
+ EXPECT_EQ((RECORD).event->type, (TYPE)); \
+ EXPECT_EQ((RECORD).event->physical, (PHYSICAL)); \
+ EXPECT_EQ((RECORD).event->logical, (LOGICAL)); \
+ EXPECT_STREQ((RECORD).event->character, (CHAR)); \
+ EXPECT_EQ((RECORD).event->synthesized, (SYNTHESIZED));
+
+#define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR) \
+ EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder); \
+ EXPECT_EQ(call_records[0].event->type, kFlutterKeyEventTypeDown); \
+ EXPECT_EQ(call_records[0].event->logical, (OUT_LOGICAL)); \
+ EXPECT_STREQ(call_records[0].event->character, (OUT_CHAR)); \
+ EXPECT_EQ(call_records[0].event->synthesized, false); \
+ call_records.clear()
+
+namespace {
+using ::flutter::testing::keycodes::kLogicalAltLeft;
+using ::flutter::testing::keycodes::kLogicalBracketLeft;
+using ::flutter::testing::keycodes::kLogicalComma;
+using ::flutter::testing::keycodes::kLogicalControlLeft;
+using ::flutter::testing::keycodes::kLogicalDigit1;
+using ::flutter::testing::keycodes::kLogicalKeyA;
+using ::flutter::testing::keycodes::kLogicalKeyB;
+using ::flutter::testing::keycodes::kLogicalKeyM;
+using ::flutter::testing::keycodes::kLogicalKeyQ;
+using ::flutter::testing::keycodes::kLogicalMetaLeft;
+using ::flutter::testing::keycodes::kLogicalMinus;
+using ::flutter::testing::keycodes::kLogicalParenthesisRight;
+using ::flutter::testing::keycodes::kLogicalSemicolon;
+using ::flutter::testing::keycodes::kLogicalShiftLeft;
+using ::flutter::testing::keycodes::kLogicalUnderscore;
+
+using ::flutter::testing::keycodes::kPhysicalAltLeft;
+using ::flutter::testing::keycodes::kPhysicalControlLeft;
+using ::flutter::testing::keycodes::kPhysicalKeyA;
+using ::flutter::testing::keycodes::kPhysicalKeyB;
+using ::flutter::testing::keycodes::kPhysicalMetaLeft;
+using ::flutter::testing::keycodes::kPhysicalShiftLeft;
+
+// Hardware key codes.
+typedef std::function<void(bool handled)> AsyncKeyCallback;
+typedef std::function<void(AsyncKeyCallback callback)> ChannelCallHandler;
+typedef std::function<void(const FlutterKeyEvent* event,
+ AsyncKeyCallback callback)>
+ EmbedderCallHandler;
+typedef std::function<void(FlKeyEvent*)> RedispatchHandler;
+
+// A type that can record all kinds of effects that the keyboard handler
+// triggers.
+//
+// An instance of `CallRecord` might not have all the fields filled.
+typedef struct {
+ enum {
+ kKeyCallEmbedder,
+ kKeyCallChannel,
+ } type;
+
+ AsyncKeyCallback callback;
+ std::unique_ptr<FlutterKeyEvent> event;
+ std::unique_ptr<char[]> event_character;
+} CallRecord;
+
+// Clone a C-string.
+//
+// Must be deleted by delete[].
+char* cloneString(const char* source) {
+ if (source == nullptr) {
+ return nullptr;
+ }
+ size_t charLen = strlen(source);
+ char* target = new char[charLen + 1];
+ strncpy(target, source, charLen + 1);
+ return target;
+}
+
+constexpr guint16 kKeyCodeKeyA = 0x26u;
+constexpr guint16 kKeyCodeKeyB = 0x38u;
+constexpr guint16 kKeyCodeKeyM = 0x3au;
+constexpr guint16 kKeyCodeDigit1 = 0x0au;
+constexpr guint16 kKeyCodeMinus = 0x14u;
+constexpr guint16 kKeyCodeSemicolon = 0x2fu;
+constexpr guint16 kKeyCodeKeyLeftBracket = 0x22u;
+
+static constexpr char kKeyEventChannelName[] = "flutter/keyevent";
+
+// All key clues for a keyboard layout.
+//
+// The index is (keyCode * 2 + hasShift), where each value is the character for
+// this key (GTK only supports UTF-16.) Since the maximum keycode of interest
+// is 128, it has a total of 256 entries..
+typedef std::array<uint32_t, 256> MockGroupLayoutData;
+typedef std::vector<const MockGroupLayoutData*> MockLayoutData;
+
+extern const MockLayoutData kLayoutUs;
+extern const MockLayoutData kLayoutRussian;
+extern const MockLayoutData kLayoutFrench;
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE(FlMockViewDelegate,
+ fl_mock_view_delegate,
+ FL,
+ MOCK_VIEW_DELEGATE,
+ GObject);
+
+G_DECLARE_FINAL_TYPE(FlMockKeyBinaryMessenger,
+ fl_mock_key_binary_messenger,
+ FL,
+ MOCK_KEY_BINARY_MESSENGER,
+ GObject)
+
+G_END_DECLS
+
+MATCHER_P(MethodSuccessResponse, result, "") {
+ g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+ g_autoptr(FlMethodResponse) response =
+ fl_method_codec_decode_response(FL_METHOD_CODEC(codec), arg, nullptr);
+ fl_method_response_get_result(response, nullptr);
+ if (fl_value_equal(fl_method_response_get_result(response, nullptr),
+ result)) {
+ return true;
+ }
+ *result_listener << ::testing::PrintToString(response);
+ return false;
+}
+
+/***** FlMockKeyBinaryMessenger *****/
+/* Mock a binary messenger that only processes messages from the embedding on
+ * the key event channel, and does so according to the callback set by
+ * fl_mock_key_binary_messenger_set_callback_handler */
+
+struct _FlMockKeyBinaryMessenger {
+ GObject parent_instance;
+
+ ChannelCallHandler callback_handler;
+};
+
+static void fl_mock_key_binary_messenger_iface_init(
+ FlBinaryMessengerInterface* iface);
+
+G_DEFINE_TYPE_WITH_CODE(
+ FlMockKeyBinaryMessenger,
+ fl_mock_key_binary_messenger,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE(fl_binary_messenger_get_type(),
+ fl_mock_key_binary_messenger_iface_init))
+
+static void fl_mock_key_binary_messenger_init(FlMockKeyBinaryMessenger* self) {}
+
+static void fl_mock_key_binary_messenger_dispose(GObject* object) {
+ G_OBJECT_CLASS(fl_mock_key_binary_messenger_parent_class)->dispose(object);
+}
+
+static void fl_mock_key_binary_messenger_class_init(
+ FlMockKeyBinaryMessengerClass* klass) {
+ G_OBJECT_CLASS(klass)->dispose = fl_mock_key_binary_messenger_dispose;
+}
+
+static void fl_mock_key_binary_messenger_send_on_channel(
+ FlBinaryMessenger* messenger,
+ const gchar* channel,
+ GBytes* message,
+ GCancellable* cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data) {
+ FlMockKeyBinaryMessenger* self = FL_MOCK_KEY_BINARY_MESSENGER(messenger);
+
+ if (callback != nullptr) {
+ EXPECT_STREQ(channel, kKeyEventChannelName);
+ self->callback_handler([self, cancellable, callback,
+ user_data](bool handled) {
+ g_autoptr(GTask) task =
+ g_task_new(self, cancellable, callback, user_data);
+ g_autoptr(FlValue) result = fl_value_new_map();
+ fl_value_set_string_take(result, "handled", fl_value_new_bool(handled));
+ g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
+ g_autoptr(GError) error = nullptr;
+ GBytes* data = fl_message_codec_encode_message(FL_MESSAGE_CODEC(codec),
+ result, &error);
+
+ g_task_return_pointer(task, data,
+ reinterpret_cast<GDestroyNotify>(g_bytes_unref));
+ });
+ }
+}
+
+static GBytes* fl_mock_key_binary_messenger_send_on_channel_finish(
+ FlBinaryMessenger* messenger,
+ GAsyncResult* result,
+ GError** error) {
+ return static_cast<GBytes*>(g_task_propagate_pointer(G_TASK(result), error));
+}
+
+static void fl_mock_binary_messenger_resize_channel(
+ FlBinaryMessenger* messenger,
+ const gchar* channel,
+ int64_t new_size) {
+ // Mock implementation. Do nothing.
+}
+
+static void fl_mock_binary_messenger_set_warns_on_channel_overflow(
+ FlBinaryMessenger* messenger,
+ const gchar* channel,
+ bool warns) {
+ // Mock implementation. Do nothing.
+}
+
+static void fl_mock_key_binary_messenger_iface_init(
+ FlBinaryMessengerInterface* iface) {
+ iface->set_message_handler_on_channel =
+ [](FlBinaryMessenger* messenger, const gchar* channel,
+ FlBinaryMessengerMessageHandler handler, gpointer user_data,
+ GDestroyNotify destroy_notify) {
+ EXPECT_STREQ(channel, kKeyEventChannelName);
+ // No need to mock. The key event channel expects no incoming messages
+ // from the framework.
+ };
+ iface->send_response = [](FlBinaryMessenger* messenger,
+ FlBinaryMessengerResponseHandle* response_handle,
+ GBytes* response, GError** error) -> gboolean {
+ // The key event channel expects no incoming messages from the framework,
+ // hence no responses either.
+ g_return_val_if_reached(TRUE);
+ return TRUE;
+ };
+ iface->send_on_channel = fl_mock_key_binary_messenger_send_on_channel;
+ iface->send_on_channel_finish =
+ fl_mock_key_binary_messenger_send_on_channel_finish;
+ iface->resize_channel = fl_mock_binary_messenger_resize_channel;
+ iface->set_warns_on_channel_overflow =
+ fl_mock_binary_messenger_set_warns_on_channel_overflow;
+}
+
+static FlMockKeyBinaryMessenger* fl_mock_key_binary_messenger_new() {
+ FlMockKeyBinaryMessenger* self = FL_MOCK_KEY_BINARY_MESSENGER(
+ g_object_new(fl_mock_key_binary_messenger_get_type(), NULL));
+
+ // Added to stop compiler complaining about an unused function.
+ FL_IS_MOCK_KEY_BINARY_MESSENGER(self);
+
+ return self;
+}
+
+static void fl_mock_key_binary_messenger_set_callback_handler(
+ FlMockKeyBinaryMessenger* self,
+ ChannelCallHandler handler) {
+ self->callback_handler = std::move(handler);
+}
+
+/***** FlMockViewDelegate *****/
+
+struct _FlMockViewDelegate {
+ GObject parent_instance;
+
+ FlMockKeyBinaryMessenger* messenger;
+ EmbedderCallHandler embedder_handler;
+ bool text_filter_result;
+ RedispatchHandler redispatch_handler;
+ const MockLayoutData* layout_data;
+};
+
+static void fl_mock_view_keyboard_delegate_iface_init(
+ FlKeyboardViewDelegateInterface* iface);
+
+G_DEFINE_TYPE_WITH_CODE(
+ FlMockViewDelegate,
+ fl_mock_view_delegate,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE(fl_keyboard_view_delegate_get_type(),
+ fl_mock_view_keyboard_delegate_iface_init))
+
+static void fl_mock_view_delegate_init(FlMockViewDelegate* self) {}
+
+static void fl_mock_view_delegate_dispose(GObject* object) {
+ FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(object);
+
+ g_clear_object(&self->messenger);
+
+ G_OBJECT_CLASS(fl_mock_view_delegate_parent_class)->dispose(object);
+}
+
+static void fl_mock_view_delegate_class_init(FlMockViewDelegateClass* klass) {
+ G_OBJECT_CLASS(klass)->dispose = fl_mock_view_delegate_dispose;
+}
+
+static void fl_mock_view_keyboard_send_key_event(
+ FlKeyboardViewDelegate* view_delegate,
+ const FlutterKeyEvent* event,
+ FlutterKeyEventCallback callback,
+ void* user_data) {
+ FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(view_delegate);
+ self->embedder_handler(event, [callback, user_data](bool handled) {
+ if (callback != nullptr) {
+ callback(handled, user_data);
+ }
+ });
+}
+
+static gboolean fl_mock_view_keyboard_text_filter_key_press(
+ FlKeyboardViewDelegate* view_delegate,
+ FlKeyEvent* event) {
+ FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(view_delegate);
+ return self->text_filter_result;
+}
+
+static void fl_mock_view_keyboard_redispatch_event(
+ FlKeyboardViewDelegate* view_delegate,
+ FlKeyEvent* event) {
+ FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(view_delegate);
+ if (self->redispatch_handler) {
+ self->redispatch_handler(event);
+ }
+}
+
+static guint fl_mock_view_keyboard_lookup_key(
+ FlKeyboardViewDelegate* view_delegate,
+ const GdkKeymapKey* key) {
+ FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(view_delegate);
+ guint8 group = static_cast<guint8>(key->group);
+ EXPECT_LT(group, self->layout_data->size());
+ const MockGroupLayoutData* group_layout = (*self->layout_data)[group];
+ EXPECT_TRUE(group_layout != nullptr);
+ EXPECT_TRUE(key->level == 0 || key->level == 1);
+ bool shift = key->level == 1;
+ return (*group_layout)[key->keycode * 2 + shift];
+}
+
+static void fl_mock_view_keyboard_delegate_iface_init(
+ FlKeyboardViewDelegateInterface* iface) {
+ iface->send_key_event = fl_mock_view_keyboard_send_key_event;
+ iface->text_filter_key_press = fl_mock_view_keyboard_text_filter_key_press;
+ iface->redispatch_event = fl_mock_view_keyboard_redispatch_event;
+ iface->lookup_key = fl_mock_view_keyboard_lookup_key;
+}
+
+static FlMockViewDelegate* fl_mock_view_delegate_new() {
+ FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(
+ g_object_new(fl_mock_view_delegate_get_type(), nullptr));
+
+ // Added to stop compiler complaining about an unused function.
+ FL_IS_MOCK_VIEW_DELEGATE(self);
+
+ self->messenger = fl_mock_key_binary_messenger_new();
+
+ return self;
+}
+
+static void fl_mock_view_set_embedder_handler(FlMockViewDelegate* self,
+ EmbedderCallHandler handler) {
+ self->embedder_handler = std::move(handler);
+}
+
+static void fl_mock_view_set_text_filter_result(FlMockViewDelegate* self,
+ bool result) {
+ self->text_filter_result = result;
+}
+
+static void fl_mock_view_set_redispatch_handler(FlMockViewDelegate* self,
+ RedispatchHandler handler) {
+ self->redispatch_handler = std::move(handler);
+}
+
+static void fl_mock_view_set_layout(FlMockViewDelegate* self,
+ const MockLayoutData* layout) {
+ self->layout_data = layout;
+}
+
+/***** End FlMockViewDelegate *****/
+
+class KeyboardTester {
+ public:
+ KeyboardTester() {
+ view_ = fl_mock_view_delegate_new();
+ respondToEmbedderCallsWith(false);
+ respondToChannelCallsWith(false);
+ respondToTextInputWith(false);
+ setLayout(kLayoutUs);
+
+ handler_ = fl_keyboard_manager_new(FL_BINARY_MESSENGER(view_->messenger),
+ FL_KEYBOARD_VIEW_DELEGATE(view_));
+ }
+
+ ~KeyboardTester() {
+ g_clear_object(&view_);
+ g_clear_object(&handler_);
+ g_clear_pointer(&redispatched_events_, g_ptr_array_unref);
+ }
+
+ FlKeyboardManager* handler() { return handler_; }
+
+ // Block until all GdkMainLoop messages are processed, which is basically
+ // used only for channel messages.
+ void flushChannelMessages() {
+ GMainLoop* loop = g_main_loop_new(nullptr, 0);
+ g_idle_add(_flushChannelMessagesCb, loop);
+ g_main_loop_run(loop);
+ }
+
+ // Dispatch each of the given events, expect their results to be false
+ // (unhandled), and clear the event array.
+ //
+ // Returns the number of events redispatched. If any result is unexpected
+ // (handled), return a minus number `-x` instead, where `x` is the index of
+ // the first unexpected redispatch.
+ int redispatchEventsAndClear(GPtrArray* events) {
+ guint event_count = events->len;
+ int first_error = -1;
+ during_redispatch_ = true;
+ for (guint event_id = 0; event_id < event_count; event_id += 1) {
+ FlKeyEvent* event = FL_KEY_EVENT(g_ptr_array_index(events, event_id));
+ bool handled = fl_keyboard_manager_handle_event(handler_, event);
+ EXPECT_FALSE(handled);
+ if (handled) {
+ first_error = first_error == -1 ? event_id : first_error;
+ }
+ }
+ during_redispatch_ = false;
+ g_ptr_array_set_size(events, 0);
+ return first_error < 0 ? event_count : -first_error;
+ }
+
+ void respondToEmbedderCallsWith(bool response) {
+ fl_mock_view_set_embedder_handler(
+ view_, [response, this](const FlutterKeyEvent* event,
+ const AsyncKeyCallback& callback) {
+ EXPECT_FALSE(during_redispatch_);
+ callback(response);
+ });
+ }
+
+ void recordEmbedderCallsTo(std::vector<CallRecord>& storage) {
+ fl_mock_view_set_embedder_handler(
+ view_, [&storage, this](const FlutterKeyEvent* event,
+ AsyncKeyCallback callback) {
+ EXPECT_FALSE(during_redispatch_);
+ auto new_event = std::make_unique<FlutterKeyEvent>(*event);
+ char* new_event_character = cloneString(event->character);
+ new_event->character = new_event_character;
+ storage.push_back(CallRecord{
+ .type = CallRecord::kKeyCallEmbedder,
+ .callback = std::move(callback),
+ .event = std::move(new_event),
+ .event_character = std::unique_ptr<char[]>(new_event_character),
+ });
+ });
+ }
+
+ void respondToEmbedderCallsWithAndRecordsTo(
+ bool response,
+ std::vector<CallRecord>& storage) {
+ fl_mock_view_set_embedder_handler(
+ view_, [&storage, response, this](const FlutterKeyEvent* event,
+ const AsyncKeyCallback& callback) {
+ EXPECT_FALSE(during_redispatch_);
+ auto new_event = std::make_unique<FlutterKeyEvent>(*event);
+ char* new_event_character = cloneString(event->character);
+ new_event->character = new_event_character;
+ storage.push_back(CallRecord{
+ .type = CallRecord::kKeyCallEmbedder,
+ .event = std::move(new_event),
+ .event_character = std::unique_ptr<char[]>(new_event_character),
+ });
+ callback(response);
+ });
+ }
+
+ void respondToChannelCallsWith(bool response) {
+ fl_mock_key_binary_messenger_set_callback_handler(
+ view_->messenger, [response, this](const AsyncKeyCallback& callback) {
+ EXPECT_FALSE(during_redispatch_);
+ callback(response);
+ });
+ }
+
+ void recordChannelCallsTo(std::vector<CallRecord>& storage) {
+ fl_mock_key_binary_messenger_set_callback_handler(
+ view_->messenger, [&storage, this](AsyncKeyCallback callback) {
+ EXPECT_FALSE(during_redispatch_);
+ storage.push_back(CallRecord{
+ .type = CallRecord::kKeyCallChannel,
+ .callback = std::move(callback),
+ });
+ });
+ }
+
+ void respondToTextInputWith(bool response) {
+ fl_mock_view_set_text_filter_result(view_, response);
+ }
+
+ void recordRedispatchedEventsTo(GPtrArray* storage) {
+ redispatched_events_ = g_ptr_array_ref(storage);
+ fl_mock_view_set_redispatch_handler(view_, [this](FlKeyEvent* key) {
+ g_ptr_array_add(redispatched_events_, g_object_ref(key));
+ });
+ }
+
+ void setLayout(const MockLayoutData& layout) {
+ fl_mock_view_set_layout(view_, &layout);
+ if (handler_ != nullptr) {
+ fl_keyboard_manager_notify_layout_changed(handler_);
+ }
+ }
+
+ private:
+ FlMockViewDelegate* view_;
+ FlKeyboardManager* handler_ = nullptr;
+ GPtrArray* redispatched_events_ = nullptr;
+ bool during_redispatch_ = false;
+
+ static gboolean _flushChannelMessagesCb(gpointer data) {
+ g_autoptr(GMainLoop) loop = reinterpret_cast<GMainLoop*>(data);
+ g_main_loop_quit(loop);
+ return FALSE;
+ }
+};
+
+// Make sure that the keyboard can be disposed without crashes when there are
+// unresolved pending events.
+TEST(FlKeyboardManagerTest, DisposeWithUnresolvedPends) {
+ KeyboardTester tester;
+ std::vector<CallRecord> call_records;
+
+ // Record calls so that they aren't responded.
+ tester.recordEmbedderCallsTo(call_records);
+ g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
+ 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
+ fl_keyboard_manager_handle_event(tester.handler(), event1);
+
+ tester.respondToEmbedderCallsWith(true);
+ g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
+ 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
+ fl_keyboard_manager_handle_event(tester.handler(), event2);
+
+ tester.flushChannelMessages();
+
+ // Passes if the cleanup does not crash.
+}
+
+TEST(FlKeyboardManagerTest, SingleDelegateWithAsyncResponds) {
+ KeyboardTester tester;
+ std::vector<CallRecord> call_records;
+ g_autoptr(GPtrArray) redispatched =
+ g_ptr_array_new_with_free_func(g_object_unref);
+
+ gboolean handler_handled = false;
+
+ /// Test 1: One event that is handled by the framework
+ tester.recordEmbedderCallsTo(call_records);
+ tester.recordRedispatchedEventsTo(redispatched);
+
+ // Dispatch a key event
+ g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
+ 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
+ handler_handled = fl_keyboard_manager_handle_event(tester.handler(), event1);
+ tester.flushChannelMessages();
+ EXPECT_EQ(handler_handled, true);
+ EXPECT_EQ(redispatched->len, 0u);
+ EXPECT_EQ(call_records.size(), 1u);
+ EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, kPhysicalKeyA,
+ kLogicalKeyA, "a", false);
+
+ call_records[0].callback(true);
+ tester.flushChannelMessages();
+ EXPECT_EQ(redispatched->len, 0u);
+ EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.handler()));
+ call_records.clear();
+
+ /// Test 2: Two events that are unhandled by the framework
+ g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
+ 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
+ handler_handled = fl_keyboard_manager_handle_event(tester.handler(), event2);
+ tester.flushChannelMessages();
+ EXPECT_EQ(handler_handled, true);
+ EXPECT_EQ(redispatched->len, 0u);
+ EXPECT_EQ(call_records.size(), 1u);
+ EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeUp, kPhysicalKeyA,
+ kLogicalKeyA, nullptr, false);
+
+ // Dispatch another key event
+ g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
+ 0, TRUE, kKeyCodeKeyB, GDK_KEY_b, static_cast<GdkModifierType>(0), 0);
+ handler_handled = fl_keyboard_manager_handle_event(tester.handler(), event3);
+ tester.flushChannelMessages();
+ EXPECT_EQ(handler_handled, true);
+ EXPECT_EQ(redispatched->len, 0u);
+ EXPECT_EQ(call_records.size(), 2u);
+ EXPECT_KEY_EVENT(call_records[1], kFlutterKeyEventTypeDown, kPhysicalKeyB,
+ kLogicalKeyB, "b", false);
+
+ // Resolve the second event first to test out-of-order response
+ call_records[1].callback(false);
+ EXPECT_EQ(redispatched->len, 1u);
+ EXPECT_EQ(
+ fl_key_event_get_keyval(FL_KEY_EVENT(g_ptr_array_index(redispatched, 0))),
+ 0x62u);
+ call_records[0].callback(false);
+ tester.flushChannelMessages();
+ EXPECT_EQ(redispatched->len, 2u);
+ EXPECT_EQ(
+ fl_key_event_get_keyval(FL_KEY_EVENT(g_ptr_array_index(redispatched, 1))),
+ 0x61u);
+
+ EXPECT_FALSE(fl_keyboard_manager_is_state_clear(tester.handler()));
+ call_records.clear();
+
+ // Resolve redispatches
+ EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 2);
+ tester.flushChannelMessages();
+ EXPECT_EQ(call_records.size(), 0u);
+ EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.handler()));
+
+ /// Test 3: Dispatch the same event again to ensure that prevention from
+ /// redispatching only works once.
+ g_autoptr(FlKeyEvent) event4 = fl_key_event_new(
+ 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
+ handler_handled = fl_keyboard_manager_handle_event(tester.handler(), event4);
+ tester.flushChannelMessages();
+ EXPECT_EQ(handler_handled, true);
+ EXPECT_EQ(redispatched->len, 0u);
+ EXPECT_EQ(call_records.size(), 1u);
+
+ call_records[0].callback(true);
+ EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.handler()));
+}
+
+TEST(FlKeyboardManagerTest, SingleDelegateWithSyncResponds) {
+ KeyboardTester tester;
+ gboolean handler_handled = false;
+ std::vector<CallRecord> call_records;
+ g_autoptr(GPtrArray) redispatched =
+ g_ptr_array_new_with_free_func(g_object_unref);
+
+ /// Test 1: One event that is handled by the framework
+ tester.respondToEmbedderCallsWithAndRecordsTo(true, call_records);
+ tester.recordRedispatchedEventsTo(redispatched);
+
+ // Dispatch a key event
+ g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
+ 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
+ handler_handled = fl_keyboard_manager_handle_event(tester.handler(), event1);
+ tester.flushChannelMessages();
+ EXPECT_EQ(handler_handled, true);
+ EXPECT_EQ(call_records.size(), 1u);
+ EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, kPhysicalKeyA,
+ kLogicalKeyA, "a", false);
+ EXPECT_EQ(redispatched->len, 0u);
+ call_records.clear();
+
+ EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.handler()));
+ g_ptr_array_set_size(redispatched, 0);
+
+ /// Test 2: An event unhandled by the framework
+ tester.respondToEmbedderCallsWithAndRecordsTo(false, call_records);
+ g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
+ 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
+ handler_handled = fl_keyboard_manager_handle_event(tester.handler(), event2);
+ tester.flushChannelMessages();
+ EXPECT_EQ(handler_handled, true);
+ EXPECT_EQ(call_records.size(), 1u);
+ EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeUp, kPhysicalKeyA,
+ kLogicalKeyA, nullptr, false);
+ EXPECT_EQ(redispatched->len, 1u);
+ call_records.clear();
+
+ EXPECT_FALSE(fl_keyboard_manager_is_state_clear(tester.handler()));
+
+ EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
+ EXPECT_EQ(call_records.size(), 0u);
+
+ EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.handler()));
+}
+
+TEST(FlKeyboardManagerTest, WithTwoAsyncDelegates) {
+ KeyboardTester tester;
+ std::vector<CallRecord> call_records;
+ g_autoptr(GPtrArray) redispatched =
+ g_ptr_array_new_with_free_func(g_object_unref);
+
+ gboolean handler_handled = false;
+
+ tester.recordEmbedderCallsTo(call_records);
+ tester.recordChannelCallsTo(call_records);
+ tester.recordRedispatchedEventsTo(redispatched);
+
+ /// Test 1: One delegate responds true, the other false
+
+ g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
+ 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
+ handler_handled = fl_keyboard_manager_handle_event(tester.handler(), event1);
+
+ EXPECT_EQ(handler_handled, true);
+ EXPECT_EQ(redispatched->len, 0u);
+ EXPECT_EQ(call_records.size(), 2u);
+
+ EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder);
+ EXPECT_EQ(call_records[1].type, CallRecord::kKeyCallChannel);
+
+ call_records[0].callback(true);
+ call_records[1].callback(false);
+ tester.flushChannelMessages();
+ EXPECT_EQ(redispatched->len, 0u);
+
+ EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.handler()));
+ call_records.clear();
+
+ /// Test 2: All delegates respond false
+ g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
+ 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
+ handler_handled = fl_keyboard_manager_handle_event(tester.handler(), event2);
+
+ EXPECT_EQ(handler_handled, true);
+ EXPECT_EQ(redispatched->len, 0u);
+ EXPECT_EQ(call_records.size(), 2u);
+
+ EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder);
+ EXPECT_EQ(call_records[1].type, CallRecord::kKeyCallChannel);
+
+ call_records[0].callback(false);
+ call_records[1].callback(false);
+
+ call_records.clear();
+
+ // Resolve redispatch
+ tester.flushChannelMessages();
+ EXPECT_EQ(redispatched->len, 1u);
+ EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
+ EXPECT_EQ(call_records.size(), 0u);
+
+ EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.handler()));
+}
+
+TEST(FlKeyboardManagerTest, TextInputHandlerReturnsFalse) {
+ KeyboardTester tester;
+ g_autoptr(GPtrArray) redispatched =
+ g_ptr_array_new_with_free_func(g_object_unref);
+ gboolean handler_handled = false;
+ tester.recordRedispatchedEventsTo(redispatched);
+ tester.respondToTextInputWith(false);
+
+ // Dispatch a key event.
+ g_autoptr(FlKeyEvent) event = fl_key_event_new(
+ 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
+ handler_handled = fl_keyboard_manager_handle_event(tester.handler(), event);
+ tester.flushChannelMessages();
+ EXPECT_EQ(handler_handled, true);
+ // The event was redispatched because no one handles it.
+ EXPECT_EQ(redispatched->len, 1u);
+
+ // Resolve redispatched event.
+ EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
+
+ EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.handler()));
+}
+
+TEST(FlKeyboardManagerTest, TextInputHandlerReturnsTrue) {
+ KeyboardTester tester;
+ g_autoptr(GPtrArray) redispatched =
+ g_ptr_array_new_with_free_func(g_object_unref);
+ gboolean handler_handled = false;
+ tester.recordRedispatchedEventsTo(redispatched);
+ tester.respondToTextInputWith(true);
+
+ // Dispatch a key event.
+ g_autoptr(FlKeyEvent) event = fl_key_event_new(
+ 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
+ handler_handled = fl_keyboard_manager_handle_event(tester.handler(), event);
+ tester.flushChannelMessages();
+ EXPECT_EQ(handler_handled, true);
+ // The event was not redispatched because handler handles it.
+ EXPECT_EQ(redispatched->len, 0u);
+
+ EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.handler()));
+}
+
+TEST(FlKeyboardManagerTest, CorrectLogicalKeyForLayouts) {
+ KeyboardTester tester;
+
+ std::vector<CallRecord> call_records;
+ tester.recordEmbedderCallsTo(call_records);
+
+ auto sendTap = [&](guint8 keycode, guint keyval, guint8 group) {
+ g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
+ 0, TRUE, keycode, keyval, static_cast<GdkModifierType>(0), group);
+ fl_keyboard_manager_handle_event(tester.handler(), event1);
+ g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
+ 0, FALSE, keycode, keyval, static_cast<GdkModifierType>(0), group);
+ fl_keyboard_manager_handle_event(tester.handler(), event2);
+ };
+
+ /* US keyboard layout */
+
+ sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
+ VERIFY_DOWN(kLogicalKeyA, "a");
+
+ sendTap(kKeyCodeKeyA, GDK_KEY_A, 0); // Shift-KeyA
+ VERIFY_DOWN(kLogicalKeyA, "A");
+
+ sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
+ VERIFY_DOWN(kLogicalDigit1, "1");
+
+ sendTap(kKeyCodeDigit1, GDK_KEY_exclam, 0); // Shift-Digit1
+ VERIFY_DOWN(kLogicalDigit1, "!");
+
+ sendTap(kKeyCodeMinus, GDK_KEY_minus, 0); // Minus
+ VERIFY_DOWN(kLogicalMinus, "-");
+
+ sendTap(kKeyCodeMinus, GDK_KEY_underscore, 0); // Shift-Minus
+ VERIFY_DOWN(kLogicalUnderscore, "_");
+
+ /* French keyboard layout, group 3, which is when the input method is showing
+ * "Fr" */
+
+ tester.setLayout(kLayoutFrench);
+
+ sendTap(kKeyCodeKeyA, GDK_KEY_q, 3); // KeyA
+ VERIFY_DOWN(kLogicalKeyQ, "q");
+
+ sendTap(kKeyCodeKeyA, GDK_KEY_Q, 3); // Shift-KeyA
+ VERIFY_DOWN(kLogicalKeyQ, "Q");
+
+ sendTap(kKeyCodeSemicolon, GDK_KEY_m, 3); // ; but prints M
+ VERIFY_DOWN(kLogicalKeyM, "m");
+
+ sendTap(kKeyCodeKeyM, GDK_KEY_comma, 3); // M but prints ,
+ VERIFY_DOWN(kLogicalComma, ",");
+
+ sendTap(kKeyCodeDigit1, GDK_KEY_ampersand, 3); // Digit1
+ VERIFY_DOWN(kLogicalDigit1, "&");
+
+ sendTap(kKeyCodeDigit1, GDK_KEY_1, 3); // Shift-Digit1
+ VERIFY_DOWN(kLogicalDigit1, "1");
+
+ sendTap(kKeyCodeMinus, GDK_KEY_parenright, 3); // Minus
+ VERIFY_DOWN(kLogicalParenthesisRight, ")");
+
+ sendTap(kKeyCodeMinus, GDK_KEY_degree, 3); // Shift-Minus
+ VERIFY_DOWN(static_cast<uint32_t>(L'°'), "°");
+
+ /* French keyboard layout, group 0, which is pressing the "extra key for
+ * triggering input method" key once after switching to French IME. */
+
+ sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
+ VERIFY_DOWN(kLogicalKeyA, "a");
+
+ sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
+ VERIFY_DOWN(kLogicalDigit1, "1");
+
+ /* Russian keyboard layout, group 2 */
+ tester.setLayout(kLayoutRussian);
+
+ sendTap(kKeyCodeKeyA, GDK_KEY_Cyrillic_ef, 2); // KeyA
+ VERIFY_DOWN(kLogicalKeyA, "ф");
+
+ sendTap(kKeyCodeDigit1, GDK_KEY_1, 2); // Shift-Digit1
+ VERIFY_DOWN(kLogicalDigit1, "1");
+
+ sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_Cyrillic_ha, 2);
+ VERIFY_DOWN(kLogicalBracketLeft, "х");
+
+ /* Russian keyboard layout, group 0 */
+ sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
+ VERIFY_DOWN(kLogicalKeyA, "a");
+
+ sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_bracketleft, 0);
+ VERIFY_DOWN(kLogicalBracketLeft, "[");
+}
+
+TEST(FlKeyboardManagerTest, SynthesizeModifiersIfNeeded) {
+ KeyboardTester tester;
+ std::vector<CallRecord> call_records;
+ tester.recordEmbedderCallsTo(call_records);
+
+ auto verifyModifierIsSynthesized = [&](GdkModifierType mask,
+ uint64_t physical, uint64_t logical) {
+ // Modifier is pressed.
+ guint state = mask;
+ fl_keyboard_manager_sync_modifier_if_needed(tester.handler(), state, 1000);
+ EXPECT_EQ(call_records.size(), 1u);
+ EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, physical,
+ logical, NULL, true);
+ // Modifier is released.
+ state = state ^ mask;
+ fl_keyboard_manager_sync_modifier_if_needed(tester.handler(), state, 1001);
+ EXPECT_EQ(call_records.size(), 2u);
+ EXPECT_KEY_EVENT(call_records[1], kFlutterKeyEventTypeUp, physical, logical,
+ NULL, true);
+ call_records.clear();
+ };
+
+ // No modifiers pressed.
+ guint state = 0;
+ fl_keyboard_manager_sync_modifier_if_needed(tester.handler(), state, 1000);
+ EXPECT_EQ(call_records.size(), 0u);
+ call_records.clear();
+
+ // Press and release each modifier once.
+ verifyModifierIsSynthesized(GDK_CONTROL_MASK, kPhysicalControlLeft,
+ kLogicalControlLeft);
+ verifyModifierIsSynthesized(GDK_META_MASK, kPhysicalMetaLeft,
+ kLogicalMetaLeft);
+ verifyModifierIsSynthesized(GDK_MOD1_MASK, kPhysicalAltLeft, kLogicalAltLeft);
+ verifyModifierIsSynthesized(GDK_SHIFT_MASK, kPhysicalShiftLeft,
+ kLogicalShiftLeft);
+}
+
+TEST(FlKeyboardManagerTest, GetPressedState) {
+ KeyboardTester tester;
+ tester.respondToTextInputWith(true);
+
+ // Dispatch a key event.
+ g_autoptr(FlKeyEvent) event = fl_key_event_new(
+ 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
+ fl_keyboard_manager_handle_event(tester.handler(), event);
+
+ GHashTable* pressedState =
+ fl_keyboard_manager_get_pressed_state(tester.handler());
+ EXPECT_EQ(g_hash_table_size(pressedState), 1u);
+
+ gpointer physical_key =
+ g_hash_table_lookup(pressedState, uint64_to_gpointer(kPhysicalKeyA));
+ EXPECT_EQ(gpointer_to_uint64(physical_key), kLogicalKeyA);
+}
+
+// The following layout data is generated using DEBUG_PRINT_LAYOUT.
+
+const MockGroupLayoutData kLayoutUs0{{
+ // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
+ 0xffff, 0x0031, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
+ 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
+ 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
+ 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
+ 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
+ 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
+ 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
+ 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
+ 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
+ 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
+ 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
+ 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
+ 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
+ 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
+ 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
+ 0xffff, 0xffff, 0x003c, 0x003e, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x64
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
+ 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
+}};
+
+const MockGroupLayoutData kLayoutRussian0{
+ // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
+ 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
+ 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
+ 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
+ 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
+ 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
+ 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
+ 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
+ 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
+ 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
+ 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
+ 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
+ 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
+ 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
+ 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
+ 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
+ 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
+ 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
+ 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
+ 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
+};
+
+const MockGroupLayoutData kLayoutRussian2{{
+ // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
+ 0xffff, 0x0031, 0x0021, 0x0000, 0x0031, 0x0021, 0x0032, 0x0022, // 0x08
+ 0x0033, 0x06b0, 0x0034, 0x003b, 0x0035, 0x0025, 0x0036, 0x003a, // 0x0c
+ 0x0037, 0x003f, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
+ 0x002d, 0x005f, 0x003d, 0x002b, 0x0071, 0x0051, 0x0000, 0x0000, // 0x14
+ 0x06ca, 0x06ea, 0x06c3, 0x06e3, 0x06d5, 0x06f5, 0x06cb, 0x06eb, // 0x18
+ 0x06c5, 0x06e5, 0x06ce, 0x06ee, 0x06c7, 0x06e7, 0x06db, 0x06fb, // 0x1c
+ 0x06dd, 0x06fd, 0x06da, 0x06fa, 0x06c8, 0x06e8, 0x06df, 0x06ff, // 0x20
+ 0x0061, 0x0041, 0x0041, 0x0000, 0x06c6, 0x06e6, 0x06d9, 0x06f9, // 0x24
+ 0x06d7, 0x06f7, 0x06c1, 0x06e1, 0x06d0, 0x06f0, 0x06d2, 0x06f2, // 0x28
+ 0x06cf, 0x06ef, 0x06cc, 0x06ec, 0x06c4, 0x06e4, 0x06d6, 0x06f6, // 0x2c
+ 0x06dc, 0x06fc, 0x06a3, 0x06b3, 0x007c, 0x0000, 0x005c, 0x002f, // 0x30
+ 0x06d1, 0x06f1, 0x06de, 0x06fe, 0x06d3, 0x06f3, 0x06cd, 0x06ed, // 0x34
+ 0x06c9, 0x06e9, 0x06d4, 0x06f4, 0x06d8, 0x06f8, 0x06c2, 0x06e2, // 0x38
+ 0x06c0, 0x06e0, 0x002e, 0x002c, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
+ 0xffff, 0xffff, 0x003c, 0x003e, 0x002f, 0x007c, 0xffff, 0xffff, // 0x5c
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x64
+ 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0x0000, // 0x68
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, // 0x78
+ 0x00b1, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x7c
+}};
+
+const MockGroupLayoutData kLayoutFrench0 = {
+ // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
+ 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
+ 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
+ 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
+ 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
+ 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
+ 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
+ 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
+ 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
+ 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
+ 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
+ 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
+ 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
+ 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
+ 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
+ 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
+ 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
+ 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
+ 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
+ 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
+};
+
+const MockGroupLayoutData kLayoutFrench3 = {
+ // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
+ 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
+ 0x0000, 0xffff, 0x0000, 0x0000, 0x0026, 0x0031, 0x00e9, 0x0032, // 0x08
+ 0x0022, 0x0033, 0x0027, 0x0034, 0x0028, 0x0035, 0x002d, 0x0036, // 0x0c
+ 0x00e8, 0x0037, 0x005f, 0x0038, 0x00e7, 0x0039, 0x00e0, 0x0030, // 0x10
+ 0x0029, 0x00b0, 0x003d, 0x002b, 0x0000, 0x0000, 0x0061, 0x0041, // 0x14
+ 0x0061, 0x0041, 0x007a, 0x005a, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
+ 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
+ 0x006f, 0x004f, 0x0070, 0x0050, 0xffff, 0xffff, 0x0024, 0x00a3, // 0x20
+ 0x0041, 0x0000, 0x0000, 0x0000, 0x0071, 0x0051, 0x0073, 0x0053, // 0x24
+ 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
+ 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x006d, 0x004d, // 0x2c
+ 0x00f9, 0x0025, 0x00b2, 0x007e, 0x0000, 0x0000, 0x002a, 0x00b5, // 0x30
+ 0x0077, 0x0057, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
+ 0x0062, 0x0042, 0x006e, 0x004e, 0x002c, 0x003f, 0x003b, 0x002e, // 0x38
+ 0x003a, 0x002f, 0x0021, 0x00a7, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
+ 0xffff, 0x003c, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
+ 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
+ 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
+ 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, // 0x78
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x7c
+};
+
+const MockLayoutData kLayoutUs{&kLayoutUs0};
+const MockLayoutData kLayoutRussian{&kLayoutRussian0, nullptr,
+ &kLayoutRussian2};
+const MockLayoutData kLayoutFrench{&kLayoutFrench0, nullptr, nullptr,
+ &kLayoutFrench3};
+
+} // namespace
diff --git a/shell/platform/linux/fl_keyboard_view_delegate.cc b/shell/platform/linux/fl_keyboard_view_delegate.cc
index e1301c1..771e428 100644
--- a/shell/platform/linux/fl_keyboard_view_delegate.cc
+++ b/shell/platform/linux/fl_keyboard_view_delegate.cc
@@ -32,13 +32,6 @@
self, event);
}
-FlBinaryMessenger* fl_keyboard_view_delegate_get_messenger(
- FlKeyboardViewDelegate* self) {
- g_return_val_if_fail(FL_IS_KEYBOARD_VIEW_DELEGATE(self), nullptr);
-
- return FL_KEYBOARD_VIEW_DELEGATE_GET_IFACE(self)->get_messenger(self);
-}
-
void fl_keyboard_view_delegate_redispatch_event(FlKeyboardViewDelegate* self,
FlKeyEvent* event) {
g_return_if_fail(FL_IS_KEYBOARD_VIEW_DELEGATE(self));
diff --git a/shell/platform/linux/fl_keyboard_view_delegate.h b/shell/platform/linux/fl_keyboard_view_delegate.h
index eb1104f..1342390 100644
--- a/shell/platform/linux/fl_keyboard_view_delegate.h
+++ b/shell/platform/linux/fl_keyboard_view_delegate.h
@@ -42,8 +42,6 @@
gboolean (*text_filter_key_press)(FlKeyboardViewDelegate* delegate,
FlKeyEvent* event);
- FlBinaryMessenger* (*get_messenger)(FlKeyboardViewDelegate* delegate);
-
void (*redispatch_event)(FlKeyboardViewDelegate* delegate, FlKeyEvent* event);
guint (*lookup_key)(FlKeyboardViewDelegate* view_delegate,
@@ -82,17 +80,6 @@
FlKeyEvent* event);
/**
- * fl_keyboard_view_delegate_get_messenger:
- *
- * Returns a binary messenger that can be used to send messages to the
- * framework.
- *
- * The ownership of messenger is kept by the view delegate.
- */
-FlBinaryMessenger* fl_keyboard_view_delegate_get_messenger(
- FlKeyboardViewDelegate* delegate);
-
-/**
* fl_keyboard_view_delegate_redispatch_event:
*
* Handles `FlKeyboardHandler`'s request to insert a GDK event to the system for
diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc
index 6e44ecb..3828f8e 100644
--- a/shell/platform/linux/fl_view.cc
+++ b/shell/platform/linux/fl_view.cc
@@ -17,6 +17,7 @@
#include "flutter/shell/platform/linux/fl_framebuffer.h"
#include "flutter/shell/platform/linux/fl_key_event.h"
#include "flutter/shell/platform/linux/fl_keyboard_handler.h"
+#include "flutter/shell/platform/linux/fl_keyboard_manager.h"
#include "flutter/shell/platform/linux/fl_keyboard_view_delegate.h"
#include "flutter/shell/platform/linux/fl_mouse_cursor_handler.h"
#include "flutter/shell/platform/linux/fl_platform_handler.h"
@@ -63,6 +64,8 @@
FlScrollingManager* scrolling_manager;
+ FlKeyboardManager* keyboard_manager;
+
// Flutter system channel handlers.
FlKeyboardHandler* keyboard_handler;
FlTextInputHandler* text_input_handler;
@@ -151,6 +154,9 @@
g_clear_object(&self->keyboard_handler);
self->keyboard_handler =
fl_keyboard_handler_new(messenger, FL_KEYBOARD_VIEW_DELEGATE(self));
+ g_clear_object(&self->keyboard_manager);
+ self->keyboard_manager =
+ fl_keyboard_manager_new(messenger, FL_KEYBOARD_VIEW_DELEGATE(self));
}
static void init_scrolling(FlView* self) {
@@ -230,7 +236,7 @@
gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
fl_scrolling_manager_set_last_mouse_position(
self->scrolling_manager, event_x * scale_factor, event_y * scale_factor);
- fl_keyboard_handler_sync_modifier_if_needed(self->keyboard_handler,
+ fl_keyboard_manager_sync_modifier_if_needed(self->keyboard_manager,
event_state, event_time);
fl_engine_send_mouse_pointer_event(
self->engine, self->view_id, phase,
@@ -379,11 +385,6 @@
event);
};
- iface->get_messenger = [](FlKeyboardViewDelegate* view_delegate) {
- FlView* self = FL_VIEW(view_delegate);
- return fl_engine_get_binary_messenger(self->engine);
- };
-
iface->redispatch_event = [](FlKeyboardViewDelegate* view_delegate,
FlKeyEvent* event) {
GdkEventType event_type =
@@ -500,7 +501,7 @@
gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
- fl_keyboard_handler_sync_modifier_if_needed(self->keyboard_handler,
+ fl_keyboard_manager_sync_modifier_if_needed(self->keyboard_manager,
event_state, event_time);
fl_engine_send_mouse_pointer_event(
self->engine, self->view_id, self->button_state != 0 ? kMove : kHover,
@@ -559,7 +560,7 @@
}
static void keymap_keys_changed_cb(FlView* self) {
- fl_keyboard_handler_notify_layout_changed(self->keyboard_handler);
+ fl_keyboard_manager_notify_layout_changed(self->keyboard_manager);
}
static void gesture_rotation_begin_cb(FlView* self) {
@@ -728,11 +729,12 @@
g_clear_pointer(&self->background_color, gdk_rgba_free);
g_clear_object(&self->window_state_monitor);
g_clear_object(&self->scrolling_manager);
- g_clear_object(&self->keyboard_handler);
+ g_clear_object(&self->keyboard_manager);
if (self->keymap_keys_changed_cb_id != 0) {
g_signal_handler_disconnect(self->keymap, self->keymap_keys_changed_cb_id);
self->keymap_keys_changed_cb_id = 0;
}
+ g_clear_object(&self->keyboard_handler);
g_clear_object(&self->mouse_cursor_handler);
g_clear_object(&self->platform_handler);
g_clear_object(&self->view_accessible);
@@ -755,8 +757,8 @@
static gboolean fl_view_key_press_event(GtkWidget* widget, GdkEventKey* event) {
FlView* self = FL_VIEW(widget);
- return fl_keyboard_handler_handle_event(
- self->keyboard_handler, fl_key_event_new_from_gdk_event(gdk_event_copy(
+ return fl_keyboard_manager_handle_event(
+ self->keyboard_manager, fl_key_event_new_from_gdk_event(gdk_event_copy(
reinterpret_cast<GdkEvent*>(event))));
}
@@ -764,8 +766,8 @@
static gboolean fl_view_key_release_event(GtkWidget* widget,
GdkEventKey* event) {
FlView* self = FL_VIEW(widget);
- return fl_keyboard_handler_handle_event(
- self->keyboard_handler, fl_key_event_new_from_gdk_event(gdk_event_copy(
+ return fl_keyboard_manager_handle_event(
+ self->keyboard_manager, fl_key_event_new_from_gdk_event(gdk_event_copy(
reinterpret_cast<GdkEvent*>(event))));
}
@@ -911,5 +913,5 @@
GHashTable* fl_view_get_keyboard_state(FlView* self) {
g_return_val_if_fail(FL_IS_VIEW(self), nullptr);
- return fl_keyboard_handler_get_pressed_state(self->keyboard_handler);
+ return fl_keyboard_manager_get_pressed_state(self->keyboard_manager);
}
diff --git a/sky/packages/sky_engine/LICENSE b/sky/packages/sky_engine/LICENSE
index 1d758e4..82c2017 100644
--- a/sky/packages/sky_engine/LICENSE
+++ b/sky/packages/sky_engine/LICENSE
@@ -32054,7 +32054,7 @@
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
-You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/d916a5f69a486de98316900f19ef0ff46834b03d
+You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/993d3069f42e98b2b29e441bc98424065cc255ca
/third_party/fallback_root_certificates/
--------------------------------------------------------------------------------
diff --git a/testing/impeller_golden_tests_output.txt b/testing/impeller_golden_tests_output.txt
index 8952a74..ef3b466 100644
--- a/testing/impeller_golden_tests_output.txt
+++ b/testing/impeller_golden_tests_output.txt
@@ -545,6 +545,9 @@
impeller_Play_AiksTest_CoverageOriginShouldBeAccountedForInSubpasses_Metal.png
impeller_Play_AiksTest_CoverageOriginShouldBeAccountedForInSubpasses_OpenGLES.png
impeller_Play_AiksTest_CoverageOriginShouldBeAccountedForInSubpasses_Vulkan.png
+impeller_Play_AiksTest_DestructiveBlendColorFilterFloodsClip_Metal.png
+impeller_Play_AiksTest_DestructiveBlendColorFilterFloodsClip_OpenGLES.png
+impeller_Play_AiksTest_DestructiveBlendColorFilterFloodsClip_Vulkan.png
impeller_Play_AiksTest_DispatcherDoesNotCullPerspectiveTransformedChildDisplayLists_Metal.png
impeller_Play_AiksTest_DispatcherDoesNotCullPerspectiveTransformedChildDisplayLists_OpenGLES.png
impeller_Play_AiksTest_DispatcherDoesNotCullPerspectiveTransformedChildDisplayLists_Vulkan.png
diff --git a/tools/yapf.sh b/tools/yapf.sh
index 48d0974..bab0071 100755
--- a/tools/yapf.sh
+++ b/tools/yapf.sh
@@ -36,5 +36,19 @@
SCRIPT_DIR=$(follow_links "$(dirname -- "${BASH_SOURCE[0]}")")
SRC_DIR="$(cd "$SCRIPT_DIR/../.."; pwd -P)"
YAPF_DIR="$(cd "$SRC_DIR/flutter/third_party/yapf"; pwd -P)"
+if command -v python3.10 &> /dev/null; then
+ PYTHON_EXEC="python3.10"
+else
+ python3 -c "
+import sys
+version = sys.version_info
+if (version.major, version.minor) > (3, 10):
+ print(f'Error: python3 version {version.major}.{version.minor} is greater than 3.10.', file=sys.stderr)
+ sys.exit(1)
+else:
+ print(f'Using python3 version {version.major}.{version.minor}.')
+" || exit 1
+ PYTHON_EXEC="python3"
+fi
-PYTHONPATH="$YAPF_DIR" python3 "$YAPF_DIR/yapf" "$@"
+PYTHONPATH="$YAPF_DIR" $PYTHON_EXEC "$YAPF_DIR/yapf" "$@"