From 9149220eb7a456768a4efc89421d1cc41b87b763 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 14:48:50 -0800 Subject: [PATCH 01/82] Split windows and embedder changes out --- lib/ui/BUILD.gn | 4 + lib/ui/dart_ui.gni | 1 + lib/ui/hooks.dart | 6 + lib/ui/key.dart | 68 ++++ lib/ui/platform_dispatcher.dart | 61 +++- lib/ui/ui.dart | 1 + lib/ui/window.dart | 9 + lib/ui/window/key_data.cc | 18 + lib/ui/window/key_data.h | 48 +++ lib/ui/window/key_data_packet.cc | 36 ++ lib/ui/window/key_data_packet.h | 66 ++++ lib/ui/window/window.cc | 16 + lib/ui/window/window.h | 2 + runtime/runtime_controller.cc | 11 + runtime/runtime_controller.h | 8 + shell/common/engine.cc | 8 + shell/common/engine.h | 11 + shell/common/platform_view.cc | 5 + shell/common/platform_view.h | 22 ++ shell/common/shell.cc | 15 + shell/common/shell.h | 4 + shell/common/shell_unittests.cc | 3 + shell/platform/embedder/embedder.cc | 59 ++++ shell/platform/embedder/embedder.h | 42 +++ shell/platform/embedder/embedder_engine.cc | 15 + shell/platform/embedder/embedder_engine.h | 3 + .../fuchsia/flutter/platform_view_unittest.cc | 3 + shell/platform/windows/BUILD.gn | 4 + shell/platform/windows/flutter_key_map.cc | 325 ++++++++++++++++++ .../windows/flutter_keyboard_manager.cc | 220 ++++++++++++ .../windows/flutter_keyboard_manager.h | 67 ++++ .../flutter_keyboard_manager_unittests.cc | 160 +++++++++ .../windows/flutter_windows_engine.cc | 6 + .../platform/windows/flutter_windows_engine.h | 3 + .../platform/windows/flutter_windows_view.cc | 17 +- shell/platform/windows/flutter_windows_view.h | 4 +- shell/platform/windows/key_event_handler.cc | 3 +- shell/platform/windows/key_event_handler.h | 3 +- .../platform/windows/keyboard_hook_handler.h | 3 +- .../windows/testing/mock_win32_window.h | 2 +- shell/platform/windows/text_input_plugin.cc | 3 +- shell/platform/windows/text_input_plugin.h | 3 +- .../platform/windows/win32_flutter_window.cc | 5 +- shell/platform/windows/win32_flutter_window.h | 2 +- shell/platform/windows/win32_window.cc | 10 +- shell/platform/windows/win32_window.h | 2 +- .../windows/window_binding_handler_delegate.h | 2 +- 47 files changed, 1367 insertions(+), 22 deletions(-) create mode 100644 lib/ui/key.dart create mode 100644 lib/ui/window/key_data.cc create mode 100644 lib/ui/window/key_data.h create mode 100644 lib/ui/window/key_data_packet.cc create mode 100644 lib/ui/window/key_data_packet.h create mode 100644 shell/platform/windows/flutter_key_map.cc create mode 100644 shell/platform/windows/flutter_keyboard_manager.cc create mode 100644 shell/platform/windows/flutter_keyboard_manager.h create mode 100644 shell/platform/windows/flutter_keyboard_manager_unittests.cc diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index fa31f9fe5575e..18521e8818a97 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -93,6 +93,10 @@ source_set("ui") { "ui_dart_state.h", "volatile_path_tracker.cc", "volatile_path_tracker.h", + "window/key_data.cc", + "window/key_data.h", + "window/key_data_packet.cc", + "window/key_data_packet.h", "window/platform_configuration.cc", "window/platform_configuration.h", "window/platform_message.cc", diff --git a/lib/ui/dart_ui.gni b/lib/ui/dart_ui.gni index 11fa74da53f84..96a7b06fbd918 100644 --- a/lib/ui/dart_ui.gni +++ b/lib/ui/dart_ui.gni @@ -10,6 +10,7 @@ dart_ui_files = [ "//flutter/lib/ui/hash_codes.dart", "//flutter/lib/ui/hooks.dart", "//flutter/lib/ui/isolate_name_server.dart", + "//flutter/lib/ui/key.dart", "//flutter/lib/ui/lerp.dart", "//flutter/lib/ui/natives.dart", "//flutter/lib/ui/painting.dart", diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index 6ffcea0f93d72..8d6770471ad71 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -96,6 +96,12 @@ void _dispatchPointerDataPacket(ByteData packet) { PlatformDispatcher.instance._dispatchPointerDataPacket(packet); } +@pragma('vm:entry-point') +// ignore: unused_element +void _dispatchKeyData(ByteData packet) { + PlatformDispatcher.instance._dispatchKeyData(packet); +} + @pragma('vm:entry-point') // ignore: unused_element void _dispatchSemanticsAction(int id, int action, ByteData? args) { diff --git a/lib/ui/key.dart b/lib/ui/key.dart new file mode 100644 index 0000000000000..e60bfbeb027f1 --- /dev/null +++ b/lib/ui/key.dart @@ -0,0 +1,68 @@ +// 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. + +// @dart = 2.12 + +part of dart.ui; + +/// How the key has changed since the last report. +/// +// Must match the KeyChange enum in ui/window/key_data.h. +enum KeyChange { + /// The key is pressed. + down, + + /// The key is released. + up, + + /// The key is held, causing a repeated key input. + repeat, +} + +/// Information about the change of a key. +class KeyData { + /// Creates an object that represents the change of a key. + const KeyData({ + required this.timeStamp, + required this.change, + required this.physical, + required this.logical, + required this.character, + required this.synthesized, + }); + + /// Time of event dispatch, relative to an arbitrary timeline. + /// + /// For [KeyChange.synchronize] and [KeyChange.cancel] events, the [timeStamp] + /// might not be the actual time that the key press or release happens. + final Duration timeStamp; + + /// How the key has changed since the last report. + final KeyChange change; + + /// The key code for the physical key that has changed. + final int physical; + + /// The key code for the logical key that has changed. + final int logical; + + /// Character input from the event. + /// + /// Not available to up events. + final String? character; + + /// Whether the event is generated by Flutter, hence not corresponding to a + /// native event. + final bool synthesized; + + @override + String toString() => 'KeyData(timeStamp: $timeStamp, change: $change, physical: $physical, ' + 'logical: $logical, character: $character, synthesized: $synthesized)'; + + /// Returns a complete textual description of the information in this object. + String toStringFull() { + return '$runtimeType(' + ')'; + } +} diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index dc7223b79cc5d..c93fe976cc518 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -28,6 +28,9 @@ typedef TimingsCallback = void Function(List timings); /// Signature for [PlatformDispatcher.onPointerDataPacket]. typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); +/// Signature for [PlatformDispatcher.onKeyData]. +typedef KeyDataCallback = void Function(KeyData data); + /// Signature for [PlatformDispatcher.onSemanticsAction]. typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); @@ -332,6 +335,62 @@ class PlatformDispatcher { return PointerDataPacket(data: data); } + /// A callback that is invoked when key data is available. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + KeyDataCallback? get onKeyData => _onKeyData; + KeyDataCallback? _onKeyData; + Zone _onKeyDataZone = Zone.root; + set onKeyData(KeyDataCallback? callback) { + _onKeyData = callback; + _onKeyDataZone = Zone.current; + } + + // Called from the engine, via hooks.dart + void _dispatchKeyData(ByteData packet) { + print('dispatch: onKeyData $onKeyData'); + if (onKeyData != null) { + _invoke1( + onKeyData, + _onKeyDataZone, + _unpackKeyData(packet), + ); + } + } + + // If this value changes, update the encoding code in the following files: + // + // * key_data.h + // * key.dart (ui) + // * key.dart (web_ui) + // * HardwareKeyboard.java + static const int _kKeyDataFieldCount = 5; + + // KeyData packet structure: + // | CharDataSize | (1 field) + // | Key Data | (_kKeyDataFieldCount fields) + // | CharData | (CharDataSize bits) + static KeyData _unpackKeyData(ByteData packet) { + const int kStride = Int64List.bytesPerElement; + + int offset = 0; + final int charDataSize = packet.getUint64(kStride * offset++, _kFakeHostEndian); + final String? character = charDataSize == 0 ? null : utf8.decoder.convert( + packet.buffer.asUint8List(kStride * (offset + _kKeyDataFieldCount), charDataSize)); + + final KeyData keyData = KeyData( + timeStamp: Duration(microseconds: packet.getUint64(kStride * offset++, _kFakeHostEndian)), + change: KeyChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], + physical: packet.getUint64(kStride * offset++, _kFakeHostEndian), + logical: packet.getUint64(kStride * offset++, _kFakeHostEndian), + character: character, + synthesized: packet.getUint64(kStride * offset++, _kFakeHostEndian) != 0, + ); + + return keyData; + } + /// A callback that is invoked to report the [FrameTiming] of recently /// rasterized frames. /// @@ -1547,4 +1606,4 @@ class Locale { out.write('$separator$countryCode'); return out.toString(); } -} \ No newline at end of file +} diff --git a/lib/ui/ui.dart b/lib/ui/ui.dart index ea966c88cd43a..f48b8162ba42c 100644 --- a/lib/ui/ui.dart +++ b/lib/ui/ui.dart @@ -30,6 +30,7 @@ part 'geometry.dart'; part 'hash_codes.dart'; part 'hooks.dart'; part 'isolate_name_server.dart'; +part 'key.dart'; part 'lerp.dart'; part 'natives.dart'; part 'painting.dart'; diff --git a/lib/ui/window.dart b/lib/ui/window.dart index ac984a2a6581c..da65c1d625a5a 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -556,6 +556,15 @@ class SingletonFlutterWindow extends FlutterWindow { platformDispatcher.onPointerDataPacket = callback; } + /// A callback that is invoked when key data is available. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + KeyDataCallback? get onKeyData => platformDispatcher.onKeyData; + set onKeyData(KeyDataCallback? callback) { + platformDispatcher.onKeyData = callback; + } + /// The route or path that the embedder requested when the application was /// launched. /// diff --git a/lib/ui/window/key_data.cc b/lib/ui/window/key_data.cc new file mode 100644 index 0000000000000..875f89f7522c2 --- /dev/null +++ b/lib/ui/window/key_data.cc @@ -0,0 +1,18 @@ +// 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/lib/ui/window/key_data.h" + +#include + +namespace flutter { + +static_assert(sizeof(KeyData) == kBytesPerKeyField * kKeyDataFieldCount, + "KeyData has the wrong size"); + +void KeyData::Clear() { + memset(this, 0, sizeof(KeyData)); +} + +} // namespace flutter diff --git a/lib/ui/window/key_data.h b/lib/ui/window/key_data.h new file mode 100644 index 0000000000000..d41c162f47f8a --- /dev/null +++ b/lib/ui/window/key_data.h @@ -0,0 +1,48 @@ +// 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_LIB_UI_WINDOW_KEY_DATA_H_ +#define FLUTTER_LIB_UI_WINDOW_KEY_DATA_H_ + +#include + +namespace flutter { + +// If this value changes, update the key data unpacking code in hooks.dart. +static constexpr int kKeyDataFieldCount = 5; +static constexpr int kBytesPerKeyField = sizeof(int64_t); + +// The change of the key event, used by KeyData. +// +// Must match the KeyChange enum in ui/key.dart. +enum class KeyChange : int64_t { + kDown = 0, + kUp, + kRepeat, +}; + +// The fixed-length sections of a KeyDataPacket. +// +// KeyData does not contain `character`, for various-length data are stored in a +// different way in KeyDataPacket. +// +// This structure is unpacked by hooks.dart. +struct alignas(8) KeyData { + // Timestamp in microseconds from an arbitrary and consistant start point + uint64_t timestamp; + KeyChange change; + uint64_t physical; + uint64_t logical; + // True if the event does not correspond to a native event. + // + // The value is 1 for true, and 0 for false. + uint64_t synthesized; + + // Set all contents of `Keydata` to 0. + void Clear(); +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_H_ diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc new file mode 100644 index 0000000000000..6bcc0a6e08674 --- /dev/null +++ b/lib/ui/window/key_data_packet.cc @@ -0,0 +1,36 @@ +// 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/lib/ui/window/key_data_packet.h" +#include "flutter/fml/logging.h" + +namespace flutter { + +KeyDataPacket::KeyDataPacket(uint8_t* data, size_t num_bytes) + : data_(data, data + num_bytes) {} + +KeyDataPacket::KeyDataPacket(size_t num_bytes) + : data_(num_bytes) {} + +KeyDataPacket::~KeyDataPacket() = default; + +KeyDataPacketBuilder::KeyDataPacketBuilder(size_t character_data_size) + : KeyDataPacket(sizeof(uint64_t) + sizeof(KeyData) + character_data_size) { + uint64_t size64 = character_data_size; + memcpy(&data()[CharacterSizeStart_()], &size64, sizeof(size64)); +} + +KeyDataPacketBuilder::~KeyDataPacketBuilder() = default; + +void KeyDataPacketBuilder::SetKeyData(const KeyData& event) { + memcpy(&data()[KeyDataStart_()], &event, sizeof(KeyData)); +} + +void KeyDataPacketBuilder::SetCharacter(const char* character) { + if (character != nullptr) { + memcpy(data().data() + CharacterStart_(), character, data().size() - CharacterStart_()); + } +} + +} // namespace flutter diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h new file mode 100644 index 0000000000000..1f3e55baec214 --- /dev/null +++ b/lib/ui/window/key_data_packet.h @@ -0,0 +1,66 @@ +// 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_LIB_UI_WINDOW_KEY_DATA_PACKET_H_ +#define FLUTTER_LIB_UI_WINDOW_KEY_DATA_PACKET_H_ + +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/lib/ui/window/key_data.h" + + +namespace flutter { + +// Bitstream that contains a KeyData. +// +// This class provides interface to read the opaque bits. For constructing such +// a bitstream from KeyData, checkout KeyDataPacketBuilder. +class KeyDataPacket { + public: + KeyDataPacket(uint8_t* data, size_t num_bytes); + + protected: + KeyDataPacket(size_t num_bytes); + + std::vector& data() { return data_; } + + public: + ~KeyDataPacket(); + + const std::vector& data() const { return data_; } + + private: + std::vector data_; + + FML_DISALLOW_COPY_AND_ASSIGN(KeyDataPacket); +}; + +// Build a KeyDataPacket bitstream gradually. +class KeyDataPacketBuilder : public KeyDataPacket { + public: + // Build a KeyDataPacket by incrementally fill in data. + // + // The `character_data_size` is number of bytes to contain the character data. + KeyDataPacketBuilder(size_t character_data_size); + ~KeyDataPacketBuilder(); + + void SetKeyData(const KeyData& event); + + // Set character data to the proper position, which should not be terminated + // by a null character (length controled by character_data_size). + void SetCharacter(const char* characters); + + private: + size_t CharacterSizeStart_() { return 0; } + size_t KeyDataStart_() { return CharacterSizeStart_() + sizeof(uint64_t); } + size_t CharacterStart_() { return KeyDataStart_() + sizeof(KeyData); } + + FML_DISALLOW_COPY_AND_ASSIGN(KeyDataPacketBuilder); +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_PACKET_H_ diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 082df1b823d32..51a4c29724af4 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -36,6 +36,22 @@ void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) { library_.value(), "_dispatchPointerDataPacket", {data_handle})); } +void Window::DispatchKeyDataPacket(const KeyDataPacket& packet) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + + const std::vector& buffer = packet.data(); + Dart_Handle data_handle = + tonic::DartByteData::Create(buffer.data(), buffer.size()); + if (Dart_IsError(data_handle)) { + return; + } + tonic::LogIfError(tonic::DartInvokeField( + library_.value(), "_dispatchKeyData", {data_handle})); +} + void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { viewport_metrics_ = metrics; diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index b6fa2555b04d7..7080addeff6ef 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -10,6 +10,7 @@ #include #include "flutter/lib/ui/window/platform_message.h" +#include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/viewport_metrics.h" #include "third_party/skia/include/gpu/GrDirectContext.h" @@ -27,6 +28,7 @@ class Window final { const ViewportMetrics& viewport_metrics() const { return viewport_metrics_; } void DispatchPointerDataPacket(const PointerDataPacket& packet); + void DispatchKeyDataPacket(const KeyDataPacket& packet); void UpdateWindowMetrics(const ViewportMetrics& metrics); private: diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 4998a9aeb3f50..6fe32fbbf2de6 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -243,6 +243,17 @@ bool RuntimeController::DispatchPointerDataPacket( return false; } +bool RuntimeController::DispatchKeyDataPacket( + const KeyDataPacket& packet) { + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataPacket", + "mode", "basic"); + platform_configuration->get_window(0)->DispatchKeyDataPacket(packet); + return true; + } + return false; +} + bool RuntimeController::DispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) { diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 5e2c26da52726..6f42f440c4b38 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -408,6 +408,14 @@ class RuntimeController : public PlatformConfigurationClient { /// bool DispatchPointerDataPacket(const PointerDataPacket& packet); + //---------------------------------------------------------------------------- + /// @brief Dispatch the specified pointer data message to the running + /// root isolate. + /// + /// @param[in] packet The key data message to dispatch to the isolate. + /// + bool DispatchKeyDataPacket(const KeyDataPacket& packet); + //---------------------------------------------------------------------------- /// @brief Dispatch the semantics action to the specified accessibility /// node. diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 84de3e25bf92c..508c5c0bcf563 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -384,6 +384,14 @@ void Engine::DispatchPointerDataPacket( pointer_data_dispatcher_->DispatchPacket(std::move(packet), trace_flow_id); } +void Engine::DispatchKeyDataPacket( + std::unique_ptr packet) { + TRACE_EVENT0("flutter", "Engine::DispatchKeyDataPacket"); + if (runtime_controller_) { + runtime_controller_->DispatchKeyDataPacket(*packet); + } +} + void Engine::DispatchSemanticsAction(int id, SemanticsAction action, std::vector args) { diff --git a/shell/common/engine.h b/shell/common/engine.h index 8041e14199add..cf2234ac6c07d 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -710,6 +710,17 @@ class Engine final : public RuntimeDelegate, void DispatchPointerDataPacket(std::unique_ptr packet, uint64_t trace_flow_id); + //---------------------------------------------------------------------------- + /// @brief Notifies the engine that the embedder has sent it a key data + /// packet. A key data packet contains one physical key event and + /// one or multiple logical key events. This call originates in + /// the platform view and the shell has forwarded the same to the + /// engine on the UI task runner here. + /// + /// @param[in] packet The key data packet. + /// + void DispatchKeyDataPacket(std::unique_ptr packet); + //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder encountered an /// accessibility related action on the specified node. This call diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 22c2b395ba76e..168087be13a35 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -42,6 +42,11 @@ void PlatformView::DispatchPointerDataPacket( pointer_data_packet_converter_.Convert(std::move(packet))); } +void PlatformView::DispatchKeyDataPacket( + std::unique_ptr packet) { + delegate_.OnPlatformViewDispatchKeyDataPacket(std::move(packet)); +} + void PlatformView::DispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) { diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 92e670677c25a..21b98be140d09 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -16,6 +16,7 @@ #include "flutter/fml/memory/weak_ptr.h" #include "flutter/lib/ui/semantics/custom_accessibility_action.h" #include "flutter/lib/ui/semantics/semantics_node.h" +#include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/pointer_data_packet_converter.h" @@ -125,6 +126,18 @@ class PlatformView { virtual void OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) = 0; + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate that the platform view has encountered + /// a key event. This key event needs to be forwarded to + /// the running root isolate hosted by the engine on the UI + /// thread. + /// + /// @param[in] packet The key data packet containing one physical key event + /// and multiple logical key events. + /// + virtual void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet) = 0; + //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view has encountered /// an accessibility related action on the specified node. This @@ -575,6 +588,15 @@ class PlatformView { /// void DispatchPointerDataPacket(std::unique_ptr packet); + //---------------------------------------------------------------------------- + /// @brief Dispatches key events from the embedder to the framework. Each + /// key data packet contains one physical event and multiple logical key + /// events. Each call to this method wakes up the UI thread. + /// + /// @param[in] packet The key data packet to dispatch to the framework. + /// + void DispatchKeyDataPacket(std::unique_ptr packet); + //-------------------------------------------------------------------------- /// @brief Used by the embedder to specify a texture that it wants the /// rasterizer to composite within the Flutter layer tree. All diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 98d8f43f2813c..8ec6525470f81 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -875,6 +875,21 @@ void Shell::OnPlatformViewDispatchPointerDataPacket( next_pointer_flow_id_++; } +// |PlatformView::Delegate| +void Shell::OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet) { + TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataPacket"); + FML_DCHECK(is_setup_); + FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetUITaskRunner()->PostTask( + fml::MakeCopyable([engine = weak_engine_, packet = std::move(packet)]() mutable { + if (engine) { + engine->DispatchKeyDataPacket(std::move(packet)); + } + })); +} + // |PlatformView::Delegate| void Shell::OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, diff --git a/shell/common/shell.h b/shell/common/shell.h index 21214f0013f34..480d8077daaa6 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -456,6 +456,10 @@ class Shell final : public PlatformView::Delegate, void OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) override; + // |PlatformView::Delegate| + void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet) override; + // |PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction( int32_t id, diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 98621f2f0f75e..d32aad267d912 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -62,6 +62,9 @@ class MockPlatformViewDelegate : public PlatformView::Delegate { MOCK_METHOD1(OnPlatformViewDispatchPointerDataPacket, void(std::unique_ptr packet)); + MOCK_METHOD1(OnPlatformViewDispatchKeyDataPacket, + void(std::unique_ptr packet)); + MOCK_METHOD3(OnPlatformViewDispatchSemanticsAction, void(int32_t id, SemanticsAction action, diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index b2b9f26a53909..ff1928be7456f 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1492,6 +1492,64 @@ FlutterEngineResult FlutterEngineSendPointerEvent( "running Flutter application."); } +inline flutter::KeyChange ToKeyChange(FlutterKeyEventKind key_change) { + switch (key_change) { + case kFlutterKeyEventKindUp: + return flutter::KeyChange::kUp; + case kFlutterKeyEventKindDown: + return flutter::KeyChange::kDown; + case kFlutterKeyEventKindRepeat: + return flutter::KeyChange::kRepeat; + } + return flutter::KeyChange::kUp; +} + +// The number of bytes that should be able to fully store character data. +// +// This is an arbitrary number that is considered sufficient, used as an +// upperbound in strnlen. +// +// Many platforms assert the character to be less than 2 int16's, i.e. 4 bytes, +// therefore the character data is asserted to be less than double the amount, +// i.e. 8 bytes. +constexpr size_t kKeyEventCharacterMaxBytes = 8; + +FlutterEngineResult FlutterEngineSendKeyEvent( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterKeyEvent* event) { + if (engine == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); + } + + if (event == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid key event."); + } + + const char* character = SAFE_ACCESS(event, character, nullptr); + + size_t character_data_size = character == nullptr ? 0 : + strnlen(character, kKeyEventCharacterMaxBytes); + + auto packet = std::make_unique(character_data_size); + + flutter::KeyData key_data; + key_data.Clear(); + key_data.timestamp = (uint64_t)SAFE_ACCESS(event, timestamp, 0); + key_data.change = ToKeyChange(SAFE_ACCESS(event, kind, FlutterKeyEventKind::kFlutterKeyEventKindUp)); + key_data.physical = SAFE_ACCESS(event, physical, 0); + key_data.logical = SAFE_ACCESS(event, logical, 0); + key_data.synthesized = !!SAFE_ACCESS(event, synthesized, false); + packet->SetKeyData(key_data); + packet->SetCharacter(character); + + return reinterpret_cast(engine) + ->DispatchKeyDataPacket(std::move(packet)) + ? kSuccess + : LOG_EMBEDDER_ERROR(kInternalInconsistency, + "Could not dispatch the key event to the " + "running Flutter application."); +} + FlutterEngineResult FlutterEngineSendPlatformMessage( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage* flutter_message) { @@ -2166,6 +2224,7 @@ FlutterEngineResult FlutterEngineGetProcAddresses( SET_PROC(RunInitialized, FlutterEngineRunInitialized); SET_PROC(SendWindowMetricsEvent, FlutterEngineSendWindowMetricsEvent); SET_PROC(SendPointerEvent, FlutterEngineSendPointerEvent); + SET_PROC(SendKeyEvent, FlutterEngineSendKeyEvent); SET_PROC(SendPlatformMessage, FlutterEngineSendPlatformMessage); SET_PROC(PlatformMessageCreateResponseHandle, FlutterPlatformMessageCreateResponseHandle); diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 71228696308cd..ec54b5f3e18f8 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -611,6 +611,38 @@ typedef struct { int64_t buttons; } FlutterPointerEvent; + +typedef enum { + kFlutterKeyEventKindUp = 1, + kFlutterKeyEventKindDown, + kFlutterKeyEventKindRepeat, +} FlutterKeyEventKind; + +typedef struct { + /// The size of this struct. Must be sizeof(FlutterKeyEvent). + size_t struct_size; + // Timestamp in microseconds. + double timestamp; + // The event kind. + FlutterKeyEventKind kind; + // The physical key of the event, distinguished by the HID code. + uint64_t physical; + // The logical key of the event, usually the effect of the key without + // modifier, but might include modifier if the information is not available. + // Can be 0 for empty. + uint64_t logical; + // Null-terminated character input from the event. Can be null. Not available + // to up events. + const char* character; + // Whether the event is synthesized by Flutter. + bool synthesized; +} FlutterKeyEvent; + +struct _FlutterPlatformKeyEventResponseHandle; +typedef struct _FlutterPlatformKeyEventResponseHandle + FlutterPlatformKeyEventResponseHandle; + + struct _FlutterPlatformMessageResponseHandle; typedef struct _FlutterPlatformMessageResponseHandle FlutterPlatformMessageResponseHandle; @@ -1549,6 +1581,12 @@ FlutterEngineResult FlutterEngineSendPointerEvent( const FlutterPointerEvent* events, size_t events_count); +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineSendKeyEvent( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterKeyEvent* event, + FlutterPlatformKeyEventResponseHandle** response_out); + FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendPlatformMessage( FLUTTER_API_SYMBOL(FlutterEngine) engine, @@ -2062,6 +2100,9 @@ typedef FlutterEngineResult (*FlutterEngineSendPointerEventFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent* events, size_t events_count); +typedef FlutterEngineResult (*FlutterEngineSendKeyEventFnPtr)( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterKeyEvent* event); typedef FlutterEngineResult (*FlutterEngineSendPlatformMessageFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage* message); @@ -2155,6 +2196,7 @@ typedef struct { FlutterEngineRunInitializedFnPtr RunInitialized; FlutterEngineSendWindowMetricsEventFnPtr SendWindowMetricsEvent; FlutterEngineSendPointerEventFnPtr SendPointerEvent; + FlutterEngineSendKeyEventFnPtr SendKeyEvent; FlutterEngineSendPlatformMessageFnPtr SendPlatformMessage; FlutterEnginePlatformMessageCreateResponseHandleFnPtr PlatformMessageCreateResponseHandle; diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index bb0aa08bb42d5..32685c7b00ca0 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -136,6 +136,21 @@ bool EmbedderEngine::DispatchPointerDataPacket( return true; } +bool EmbedderEngine::DispatchKeyDataPacket( + std::unique_ptr packet) { + if (!IsValid() || !packet) { + return false; + } + + auto platform_view = shell_->GetPlatformView(); + if (!platform_view) { + return false; + } + + platform_view->DispatchKeyDataPacket(std::move(packet)); + return true; +} + bool EmbedderEngine::SendPlatformMessage( fml::RefPtr message) { if (!IsValid() || !message) { diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index 4f38ba4410cd0..fe044e29eecb8 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -60,6 +60,9 @@ class EmbedderEngine { bool DispatchPointerDataPacket( std::unique_ptr packet); + bool DispatchKeyDataPacket( + std::unique_ptr packet); + bool SendPlatformMessage(fml::RefPtr message); bool RegisterTexture(int64_t texture); diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 7fabcbd11d1ba..76239b254a566 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -81,6 +81,9 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { void OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) {} // |flutter::PlatformView::Delegate| + void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet) {} + // |flutter::PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction(int32_t id, flutter::SemanticsAction action, std::vector args) {} diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 873125ad4ebef..507c1ba981ceb 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -48,6 +48,9 @@ source_set("flutter_windows_source") { "angle_surface_manager.h", "cursor_handler.cc", "cursor_handler.h", + "flutter_keyboard_manager.cc", + "flutter_keyboard_manager.h", + "flutter_key_map.cc", "flutter_project_bundle.cc", "flutter_project_bundle.h", "flutter_windows.cc", @@ -159,6 +162,7 @@ if (target_os == "winuwp") { sources = [ "flutter_project_bundle_unittests.cc", + "flutter_keyboard_manager_unittests.cc", "flutter_windows_engine_unittests.cc", "string_conversion_unittests.cc", "system_utils_unittests.cc", diff --git a/shell/platform/windows/flutter_key_map.cc b/shell/platform/windows/flutter_key_map.cc new file mode 100644 index 0000000000000..a0b662baf74e8 --- /dev/null +++ b/shell/platform/windows/flutter_key_map.cc @@ -0,0 +1,325 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_KEY_MAP_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_KEY_MAP_H_ + +#include "flutter/shell/platform/windows/flutter_keyboard_manager.h" + +#include + +// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT +// This file is generated by flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and +// should not be edited directly. +// +// Edit the template dev/tools/gen_keycodes/data/windows_flutter_key_map_cc.tmpl instead. +// See dev/tools/gen_keycodes/README.md for more information. + +namespace flutter { + +std::map FlutterKeyboardManager::windowsToPhysicalMap_ = { + { 0x0000e05f, 0x00010082 }, // sleep + { 0x0000e063, 0x00010083 }, // wakeUp + { 0x000000ff, 0x00070001 }, // usbErrorRollOver + { 0x000000fc, 0x00070002 }, // usbPostFail + { 0x0000001e, 0x00070004 }, // keyA + { 0x00000030, 0x00070005 }, // keyB + { 0x0000002e, 0x00070006 }, // keyC + { 0x00000020, 0x00070007 }, // keyD + { 0x00000012, 0x00070008 }, // keyE + { 0x00000021, 0x00070009 }, // keyF + { 0x00000022, 0x0007000a }, // keyG + { 0x00000023, 0x0007000b }, // keyH + { 0x00000017, 0x0007000c }, // keyI + { 0x00000024, 0x0007000d }, // keyJ + { 0x00000025, 0x0007000e }, // keyK + { 0x00000026, 0x0007000f }, // keyL + { 0x00000032, 0x00070010 }, // keyM + { 0x00000031, 0x00070011 }, // keyN + { 0x00000018, 0x00070012 }, // keyO + { 0x00000019, 0x00070013 }, // keyP + { 0x00000010, 0x00070014 }, // keyQ + { 0x00000013, 0x00070015 }, // keyR + { 0x0000001f, 0x00070016 }, // keyS + { 0x00000014, 0x00070017 }, // keyT + { 0x00000016, 0x00070018 }, // keyU + { 0x0000002f, 0x00070019 }, // keyV + { 0x00000011, 0x0007001a }, // keyW + { 0x0000002d, 0x0007001b }, // keyX + { 0x00000015, 0x0007001c }, // keyY + { 0x0000002c, 0x0007001d }, // keyZ + { 0x00000002, 0x0007001e }, // digit1 + { 0x00000003, 0x0007001f }, // digit2 + { 0x00000004, 0x00070020 }, // digit3 + { 0x00000005, 0x00070021 }, // digit4 + { 0x00000006, 0x00070022 }, // digit5 + { 0x00000007, 0x00070023 }, // digit6 + { 0x00000008, 0x00070024 }, // digit7 + { 0x00000009, 0x00070025 }, // digit8 + { 0x0000000a, 0x00070026 }, // digit9 + { 0x0000000b, 0x00070027 }, // digit0 + { 0x0000001c, 0x00070028 }, // enter + { 0x00000001, 0x00070029 }, // escape + { 0x0000000e, 0x0007002a }, // backspace + { 0x0000000f, 0x0007002b }, // tab + { 0x00000039, 0x0007002c }, // space + { 0x0000000c, 0x0007002d }, // minus + { 0x0000000d, 0x0007002e }, // equal + { 0x0000001a, 0x0007002f }, // bracketLeft + { 0x0000001b, 0x00070030 }, // bracketRight + { 0x0000002b, 0x00070031 }, // backslash + { 0x00000027, 0x00070033 }, // semicolon + { 0x00000028, 0x00070034 }, // quote + { 0x00000029, 0x00070035 }, // backquote + { 0x00000033, 0x00070036 }, // comma + { 0x00000034, 0x00070037 }, // period + { 0x00000035, 0x00070038 }, // slash + { 0x0000003a, 0x00070039 }, // capsLock + { 0x0000003b, 0x0007003a }, // f1 + { 0x0000003c, 0x0007003b }, // f2 + { 0x0000003d, 0x0007003c }, // f3 + { 0x0000003e, 0x0007003d }, // f4 + { 0x0000003f, 0x0007003e }, // f5 + { 0x00000040, 0x0007003f }, // f6 + { 0x00000041, 0x00070040 }, // f7 + { 0x00000042, 0x00070041 }, // f8 + { 0x00000043, 0x00070042 }, // f9 + { 0x00000044, 0x00070043 }, // f10 + { 0x00000057, 0x00070044 }, // f11 + { 0x00000058, 0x00070045 }, // f12 + { 0x0000e037, 0x00070046 }, // printScreen + { 0x00000046, 0x00070047 }, // scrollLock + { 0x00000045, 0x00070048 }, // pause + { 0x0000e052, 0x00070049 }, // insert + { 0x0000e047, 0x0007004a }, // home + { 0x0000e049, 0x0007004b }, // pageUp + { 0x0000e053, 0x0007004c }, // delete + { 0x0000e04f, 0x0007004d }, // end + { 0x0000e051, 0x0007004e }, // pageDown + { 0x0000e04d, 0x0007004f }, // arrowRight + { 0x0000e04b, 0x00070050 }, // arrowLeft + { 0x0000e050, 0x00070051 }, // arrowDown + { 0x0000e048, 0x00070052 }, // arrowUp + { 0x0000e045, 0x00070053 }, // numLock + { 0x0000e035, 0x00070054 }, // numpadDivide + { 0x00000037, 0x00070055 }, // numpadMultiply + { 0x0000004a, 0x00070056 }, // numpadSubtract + { 0x0000004e, 0x00070057 }, // numpadAdd + { 0x0000e01c, 0x00070058 }, // numpadEnter + { 0x0000004f, 0x00070059 }, // numpad1 + { 0x00000050, 0x0007005a }, // numpad2 + { 0x00000051, 0x0007005b }, // numpad3 + { 0x0000004b, 0x0007005c }, // numpad4 + { 0x0000004c, 0x0007005d }, // numpad5 + { 0x0000004d, 0x0007005e }, // numpad6 + { 0x00000047, 0x0007005f }, // numpad7 + { 0x00000048, 0x00070060 }, // numpad8 + { 0x00000049, 0x00070061 }, // numpad9 + { 0x00000052, 0x00070062 }, // numpad0 + { 0x00000053, 0x00070063 }, // numpadDecimal + { 0x00000056, 0x00070064 }, // intlBackslash + { 0x0000e05d, 0x00070065 }, // contextMenu + { 0x0000e05e, 0x00070066 }, // power + { 0x00000059, 0x00070067 }, // numpadEqual + { 0x00000064, 0x00070068 }, // f13 + { 0x00000065, 0x00070069 }, // f14 + { 0x00000066, 0x0007006a }, // f15 + { 0x00000067, 0x0007006b }, // f16 + { 0x00000068, 0x0007006c }, // f17 + { 0x00000069, 0x0007006d }, // f18 + { 0x0000006a, 0x0007006e }, // f19 + { 0x0000006b, 0x0007006f }, // f20 + { 0x0000006c, 0x00070070 }, // f21 + { 0x0000006d, 0x00070071 }, // f22 + { 0x0000006e, 0x00070072 }, // f23 + { 0x00000076, 0x00070073 }, // f24 + { 0x0000e03b, 0x00070075 }, // help + { 0x0000e008, 0x0007007a }, // undo + { 0x0000e017, 0x0007007b }, // cut + { 0x0000e018, 0x0007007c }, // copy + { 0x0000e00a, 0x0007007d }, // paste + { 0x0000e020, 0x0007007f }, // audioVolumeMute + { 0x0000e030, 0x00070080 }, // audioVolumeUp + { 0x0000e02e, 0x00070081 }, // audioVolumeDown + { 0x0000007e, 0x00070085 }, // numpadComma + { 0x00000073, 0x00070087 }, // intlRo + { 0x00000070, 0x00070088 }, // kanaMode + { 0x0000007d, 0x00070089 }, // intlYen + { 0x00000079, 0x0007008a }, // convert + { 0x0000007b, 0x0007008b }, // nonConvert + { 0x00000072, 0x00070090 }, // lang1 + { 0x00000071, 0x00070091 }, // lang2 + { 0x00000078, 0x00070092 }, // lang3 + { 0x00000077, 0x00070093 }, // lang4 + { 0x0000001d, 0x000700e0 }, // controlLeft + { 0x0000002a, 0x000700e1 }, // shiftLeft + { 0x00000038, 0x000700e2 }, // altLeft + { 0x0000e05b, 0x000700e3 }, // metaLeft + { 0x0000e01d, 0x000700e4 }, // controlRight + { 0x00000036, 0x000700e5 }, // shiftRight + { 0x0000e038, 0x000700e6 }, // altRight + { 0x0000e05c, 0x000700e7 }, // metaRight + { 0x0000e019, 0x000c00b5 }, // mediaTrackNext + { 0x0000e010, 0x000c00b6 }, // mediaTrackPrevious + { 0x0000e024, 0x000c00b7 }, // mediaStop + { 0x0000e02c, 0x000c00b8 }, // eject + { 0x0000e022, 0x000c00cd }, // mediaPlayPause + { 0x0000e06d, 0x000c0183 }, // mediaSelect + { 0x0000e06c, 0x000c018a }, // launchMail + { 0x0000e021, 0x000c0192 }, // launchApp2 + { 0x0000e06b, 0x000c0194 }, // launchApp1 + { 0x0000e065, 0x000c0221 }, // browserSearch + { 0x0000e032, 0x000c0223 }, // browserHome + { 0x0000e06a, 0x000c0224 }, // browserBack + { 0x0000e069, 0x000c0225 }, // browserForward + { 0x0000e068, 0x000c0226 }, // browserStop + { 0x0000e067, 0x000c0227 }, // browserRefresh + { 0x0000e066, 0x000c022a }, // browserFavorites +}; + +std::map FlutterKeyboardManager::windowsToLogicalMap_ = { + { 0x00000008, 0x00000000008 }, // BACK + { 0x00000009, 0x00000000009 }, // TAB + { 0x0000000d, 0x0000000000d }, // RETURN + { 0x0000001b, 0x0000000001b }, // ESCAPE + { 0x00000020, 0x00000000020 }, // SPACE + { 0x000000de, 0x00000000022 }, // OEM_7 + { 0x000000bc, 0x0000000002c }, // OEM_COMMA + { 0x000000bd, 0x0000000002d }, // OEM_MINUS + { 0x000000be, 0x0000000002e }, // OEM_PERIOD + { 0x000000bf, 0x0000000002f }, // OEM_2 + { 0x000000ba, 0x0000000003b }, // OEM_1 + { 0x000000bb, 0x0000000003d }, // OEM_PLUS + { 0x000000db, 0x0000000005b }, // OEM_4 + { 0x000000dc, 0x0000000005c }, // OEM_5 + { 0x000000dd, 0x0000000005d }, // OEM_6 + { 0x000000c0, 0x00000000060 }, // OEM_3 + { 0x0000002e, 0x0000000007f }, // DELETE + { 0x00000014, 0x00000000104 }, // CAPITAL + { 0x00000090, 0x0000000010a }, // NUMLOCK + { 0x00000091, 0x0000000010c }, // SCROLL + { 0x00000028, 0x00000000301 }, // DOWN + { 0x00000025, 0x00000000302 }, // LEFT + { 0x00000027, 0x00000000303 }, // RIGHT + { 0x00000026, 0x00000000304 }, // UP + { 0x00000023, 0x00000000305 }, // END + { 0x00000024, 0x00000000306 }, // HOME + { 0x00000022, 0x00000000307 }, // NEXT + { 0x00000021, 0x00000000308 }, // PRIOR + { 0x0000000c, 0x00000000401 }, // CLEAR + { 0x0000002d, 0x00000000407 }, // INSERT + { 0x0000001e, 0x00000000501 }, // ACCEPT + { 0x000000f6, 0x00000000503 }, // ATTN + { 0x00000003, 0x00000000504 }, // CANCEL + { 0x0000005d, 0x00000000505 }, // APPS + { 0x0000002b, 0x00000000506 }, // EXECUTE + { 0x0000002f, 0x00000000508 }, // HELP + { 0x00000013, 0x00000000509 }, // PAUSE + { 0x000000fa, 0x0000000050a }, // PLAY + { 0x00000029, 0x0000000050c }, // SELECT + { 0x0000001c, 0x00000000705 }, // CONVERT + { 0x0000001f, 0x0000000070b }, // MODECHANGE + { 0x00000070, 0x00000000801 }, // F1 + { 0x00000071, 0x00000000802 }, // F2 + { 0x00000072, 0x00000000803 }, // F3 + { 0x00000073, 0x00000000804 }, // F4 + { 0x00000074, 0x00000000805 }, // F5 + { 0x00000075, 0x00000000806 }, // F6 + { 0x00000076, 0x00000000807 }, // F7 + { 0x00000077, 0x00000000808 }, // F8 + { 0x00000078, 0x00000000809 }, // F9 + { 0x00000079, 0x0000000080a }, // F10 + { 0x0000007a, 0x0000000080b }, // F11 + { 0x0000007b, 0x0000000080c }, // F12 + { 0x0000007c, 0x0000000080d }, // F13 + { 0x0000007d, 0x0000000080e }, // F14 + { 0x0000007e, 0x0000000080f }, // F15 + { 0x0000007f, 0x00000000810 }, // F16 + { 0x00000080, 0x00000000811 }, // F17 + { 0x00000081, 0x00000000812 }, // F18 + { 0x00000082, 0x00000000813 }, // F19 + { 0x00000083, 0x00000000814 }, // F20 + { 0x00000084, 0x00000000815 }, // F21 + { 0x00000085, 0x00000000816 }, // F22 + { 0x00000086, 0x00000000817 }, // F23 + { 0x00000087, 0x00000000818 }, // F24 + { 0x000000b3, 0x00000000a05 }, // MEDIA_PLAY_PAUSE + { 0x000000b2, 0x00000000a07 }, // MEDIA_STOP + { 0x0000002a, 0x00000000a0c }, // PRINT + { 0x000000ae, 0x00000000a0f }, // VOLUME_DOWN + { 0x000000af, 0x00000000a10 }, // VOLUME_UP + { 0x000000ad, 0x00000000a11 }, // VOLUME_MUTE + { 0x000000b4, 0x00000000b03 }, // LAUNCH_MAIL + { 0x000000a6, 0x00000000c01 }, // BROWSER_BACK + { 0x000000ab, 0x00000000c02 }, // BROWSER_FAVORITES + { 0x000000a7, 0x00000000c03 }, // BROWSER_FORWARD + { 0x000000ac, 0x00000000c04 }, // BROWSER_HOME + { 0x000000a8, 0x00000000c05 }, // BROWSER_REFRESH + { 0x000000aa, 0x00000000c06 }, // BROWSER_SEARCH + { 0x000000a9, 0x00000000c07 }, // BROWSER_STOP + { 0x000000c3, 0x0000005ff08 }, // GAMEPAD_A + { 0x000000c4, 0x0000005ff09 }, // GAMEPAD_B + { 0x000000c5, 0x0000005ff0a }, // GAMEPAD_X + { 0x000000c6, 0x0000005ff0b }, // GAMEPAD_Y + { 0x000000c7, 0x0000005ff0c }, // GAMEPAD_RIGHT_SHOULDER + { 0x000000c8, 0x0000005ff0d }, // GAMEPAD_LEFT_SHOULDER + { 0x000000c9, 0x0000005ff0e }, // GAMEPAD_LEFT_TRIGGER + { 0x000000ca, 0x0000005ff0f }, // GAMEPAD_RIGHT_TRIGGER + { 0x000000cb, 0x0000005ff10 }, // GAMEPAD_DPAD_UP + { 0x0000005f, 0x00100010082 }, // SLEEP + { 0x00000015, 0x00100070090 }, // KANA + { 0x00000015, 0x00100070090 }, // HANGEUL + { 0x00000015, 0x00100070090 }, // HANGUL + { 0x0000006a, 0x0020000002a }, // MULTIPLY + { 0x0000006b, 0x0020000002b }, // ADD + { 0x0000006d, 0x0020000002d }, // SUBTRACT + { 0x0000006e, 0x0020000002e }, // DECIMAL + { 0x0000006f, 0x0020000002f }, // DIVIDE + { 0x00000060, 0x00200000030 }, // NUMPAD0 + { 0x00000061, 0x00200000031 }, // NUMPAD1 + { 0x00000062, 0x00200000032 }, // NUMPAD2 + { 0x00000063, 0x00200000033 }, // NUMPAD3 + { 0x00000064, 0x00200000034 }, // NUMPAD4 + { 0x00000065, 0x00200000035 }, // NUMPAD5 + { 0x00000066, 0x00200000036 }, // NUMPAD6 + { 0x00000067, 0x00200000037 }, // NUMPAD7 + { 0x00000068, 0x00200000038 }, // NUMPAD8 + { 0x00000069, 0x00200000039 }, // NUMPAD9 + { 0x00000092, 0x0020000003d }, // OEM_NEC_EQUAL + { 0x000000a4, 0x00300000102 }, // LMENU + { 0x00000011, 0x00300000105 }, // CONTROL + { 0x000000a2, 0x00300000105 }, // LCONTROL + { 0x0000005b, 0x00300000109 }, // LWIN + { 0x00000010, 0x0030000010d }, // SHIFT + { 0x000000a0, 0x0030000010d }, // LSHIFT + { 0x000000a5, 0x00400000102 }, // RMENU + { 0x000000a3, 0x00400000105 }, // RCONTROL + { 0x0000005c, 0x00400000109 }, // RWIN + { 0x000000a1, 0x0040000010d }, // RSHIFT +}; + +std::map FlutterKeyboardManager::scanCodeToLogicalMap_ = { + { 0x0000e01d, 0x0400000105 }, // ControlRight + { 0x0000e038, 0x0400000102 }, // AltRight + { 0x0000004f, 0x0200000031 }, // Numpad1 + { 0x00000050, 0x0200000032 }, // Numpad2 + { 0x00000051, 0x0200000033 }, // Numpad3 + { 0x0000004b, 0x0200000034 }, // Numpad4 + { 0x0000004c, 0x0200000035 }, // Numpad5 + { 0x0000004d, 0x0200000036 }, // Numpad6 + { 0x00000047, 0x0200000037 }, // Numpad7 + { 0x00000048, 0x0200000038 }, // Numpad8 + { 0x00000049, 0x0200000039 }, // Numpad9 + { 0x00000052, 0x0200000030 }, // Numpad0 + { 0x0000004e, 0x020000002b }, // NumpadAdd + { 0x00000053, 0x020000002e }, // NumpadDecimal + { 0x0000e035, 0x020000002f }, // NumpadDivide + { 0x00000059, 0x020000003d }, // NumpadEqual + { 0x00000037, 0x020000002a }, // NumpadMultiply + { 0x0000004a, 0x020000002d }, // NumpadSubtract +}; + +} + +#endif diff --git a/shell/platform/windows/flutter_keyboard_manager.cc b/shell/platform/windows/flutter_keyboard_manager.cc new file mode 100644 index 0000000000000..844cec76bf5d7 --- /dev/null +++ b/shell/platform/windows/flutter_keyboard_manager.cc @@ -0,0 +1,220 @@ +// 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/windows/flutter_keyboard_manager.h" + +#include +#include + +#include +#include +#include +#include + +#include "flutter/shell/platform/windows/string_conversion.h" + +namespace flutter { + +namespace { +} // namespace + +/** + * The code prefix for keys which do not have a Unicode representation. + * + * This is used by platform-specific code to generate Flutter key codes using + * HID Usage codes. + */ +constexpr uint64_t kHidPlane = 0x00100000000; + +/** + * The code prefix for keys which have a Unicode representation. + * + * This is used by platform-specific code to generate Flutter key codes. + */ +constexpr uint64_t kUnicodePlane = 0x00000000000; + +/** + */ +constexpr uint64_t kWindowsKeyIdPlane = 0x00700000000; + +/** + * Mask for the auto-generated bit portion of the key code. + * + * This is used by platform-specific code to generate new Flutter key codes + * for keys which are not recognized. + */ +constexpr uint64_t kAutogeneratedMask = 0x10000000000; + +FlutterKeyboardManager::FlutterKeyboardManager(std::function onEvent) + : onEvent_(onEvent) {} + +FlutterKeyboardManager::~FlutterKeyboardManager() = default; + +void FlutterKeyboardManager::TextHook(FlutterWindowsView* view, + const std::u16string& code_point) {} + +static bool isAsciiPrintable(int codeUnit) { + return codeUnit <= 0x7f && codeUnit >= 0x20; +} + +static bool isControlCharacter(int codeUnit) { + return (codeUnit <= 0x1f && codeUnit >= 0x00) || (codeUnit >= 0x7f && codeUnit <= 0x9f); +} + +// Transform scancodes sent by windows to scancodes written in Chromium spec. +static uint16_t normalizeScancode(int windowsScanCode) { + // Windows scancode is composed of 1 bit of "extended" and 8 bits of code, + // concatenated (by Flutter embedding), i.e. PageUp is represented as 0x149. + // In Chromium spec the extended bit is shown as 0xe000 bit, making PageUp + // 0xe049. + return (windowsScanCode & 0xff) | ((windowsScanCode & 0x100) ? 0xe000 : 0); +} + +uint64_t FlutterKeyboardManager::getPhysicalKey(int scancode) { + int chromiumScancode = normalizeScancode(scancode); + auto resultIt = windowsToPhysicalMap_.find(chromiumScancode); + if (resultIt != windowsToPhysicalMap_.end()) + return resultIt->second; + return scancode | kHidPlane; +} + +uint64_t FlutterKeyboardManager::getLogicalKey(int key, int scancode) { + // Normally logical keys should only be derived from key codes, but since some + // key codes are either 0 or ambiguous (multiple keys using the same key + // code), these keys are resolved by scan codes. + auto numpadIt = scanCodeToLogicalMap_.find(normalizeScancode(scancode)); + if (numpadIt != scanCodeToLogicalMap_.cend()) + return numpadIt->second; + + // Check if the keyCode is one we know about and have a mapping for. + auto logicalIt = windowsToLogicalMap_.find(key); + if (logicalIt != windowsToLogicalMap_.cend()) + return logicalIt->second; + + // Upper case letters should be normalized into lower case letters. + if (isAsciiPrintable(key)) { + if (isupper(key)) { + return tolower(key); + } + return key; + } + + // For keys that do not exist in the map, if it has a non-control-character + // label, then construct a new Unicode-based key from it. Don't mark it as + // autogenerated, since the label uniquely identifies an ID from the Unicode + // plane. + if (!isControlCharacter(key)) { + return key | kUnicodePlane; + } else { + // This is a non-printable key that we don't know about, so we mint a new + // code with the autogenerated bit set. + return key | kWindowsKeyIdPlane | kAutogeneratedMask; + } +} + +void FlutterKeyboardManager::cacheUtf8String(char32_t character) { + if (character == 0) { + character_cache_[0] = '\0'; + return; + } + // TODO: Correctly handle UTF-32 + std::wstring text({static_cast(character)}); + strcpy_s(character_cache_, kCharacterCacheSize, Utf8FromUtf16(text).c_str()); +} + +void FlutterKeyboardManager::KeyboardHook(FlutterWindowsView* view, + int key, + int scancode, + int action, + char32_t character, + bool was_down) { + action == WM_KEYDOWN ? 'D' : 'U', + key, scancode, character, was_down + );fflush(stdout); + const uint64_t physical_key = getPhysicalKey(scancode); + const uint64_t logical_key = getLogicalKey(key, scancode); + assert(action == WM_KEYDOWN || action == WM_KEYUP); + const bool is_physical_down = action == WM_KEYDOWN; + + auto last_logical_record_iter = pressingRecords_.find(physical_key); + const bool had_record = last_logical_record_iter != pressingRecords_.end(); + const uint64_t last_logical_record = had_record ? last_logical_record_iter->second : 0; + + // The resulting event's `change`. + FlutterKeyEventKind change; + // The resulting event's `logical_key`. + uint64_t result_logical_key; + // The next value of pressingRecords_[physical_key] (or to remove it). + uint64_t next_logical_record; + bool next_has_record = true; + + if (is_physical_down) { + if (had_record) { + if (was_down) { + // A normal repeated key. + change = kFlutterKeyEventKindRepeat; + assert(had_record); + cacheUtf8String(character); + next_logical_record = last_logical_record; + result_logical_key = last_logical_record; + } else { + // A non-repeated key has been pressed that has the exact physical key as + // a currently pressed one, usually indicating multiple keyboards are + // pressing keys with the same physical key, or the up event was lost + // during a loss of focus. The down event is ignored. + return; + } + } else { + // A normal down event (whether the system event is a repeat or not). + change = kFlutterKeyEventKindDown; + assert(!had_record); + cacheUtf8String(character); + next_logical_record = logical_key; + result_logical_key = logical_key; + } + } else { // isPhysicalDown is false + if (last_logical_record == 0) { + // The physical key has been released before. It indicates multiple + // keyboards pressed keys with the same physical key. Ignore the up event. + return; + } else { + // A normal up event. + change = kFlutterKeyEventKindUp; + assert(had_record); + // Up events never have character. + character_cache_[0] = '\0'; + next_has_record = false; + result_logical_key = last_logical_record; + } + } + + if (next_has_record) { + pressingRecords_[physical_key] = next_logical_record; + } else { + pressingRecords_.erase(last_logical_record_iter); + } + + if (result_logical_key == VK_PROCESSKEY) { + // VK_PROCESSKEY means that the key press is used by an IME. These key + // presses are considered handled and not sent to Flutter. These events must + // be filtered by result_logical_key because the key up event of such + // presses uses the "original" logical key. + return; + } + + FlutterKeyEvent key_data = {}; + key_data.struct_size = sizeof(FlutterKeyEvent); + key_data.timestamp = + std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count(); + key_data.kind = change; + key_data.physical = physical_key; + key_data.logical = result_logical_key; + key_data.character = character_cache_; + key_data.synthesized = false; + onEvent_(key_data); +} + +} // namespace flutter diff --git a/shell/platform/windows/flutter_keyboard_manager.h b/shell/platform/windows/flutter_keyboard_manager.h new file mode 100644 index 0000000000000..42c11d04f6e9b --- /dev/null +++ b/shell/platform/windows/flutter_keyboard_manager.h @@ -0,0 +1,67 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_KEYBOARD_MANAGER_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_KEYBOARD_MANAGER_H_ + +#include +#include +#include +#include + +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/windows/keyboard_hook_handler.h" +#include "flutter/shell/platform/windows/public/flutter_windows.h" + +namespace flutter { + +namespace { +// An arbitrary size for the character cache in bytes. +// +// It should hold a UTF-32 character encoded in UTF-8 as long as the trailing +// '\0'. +constexpr size_t kCharacterCacheSize = 8; +} + +class FlutterWindowsView; + +// Implements a KeyboardHookHandler +// +// Handles key events and forwards them to the Flutter engine. +class FlutterKeyboardManager : public KeyboardHookHandler { + public: + explicit FlutterKeyboardManager(std::function onEvent); + + virtual ~FlutterKeyboardManager(); + + // |KeyboardHookHandler| + void KeyboardHook(FlutterWindowsView* window, + int key, + int scancode, + int action, + char32_t character, + bool wasDown) override; + + // |KeyboardHookHandler| + void TextHook(FlutterWindowsView* window, + const std::u16string& text) override; + + private: + void cacheUtf8String(char32_t ch); + + std::function onEvent_; + std::map pressingRecords_; + char character_cache_[kCharacterCacheSize]; + + static uint64_t getPhysicalKey(int scancode); + static uint64_t getLogicalKey(int key, int scancode); + + static std::map windowsToPhysicalMap_; + static std::map windowsToLogicalMap_; + static std::map scanCodeToLogicalMap_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_KEYBOARD_MANAGER_H_ diff --git a/shell/platform/windows/flutter_keyboard_manager_unittests.cc b/shell/platform/windows/flutter_keyboard_manager_unittests.cc new file mode 100644 index 0000000000000..481ec220a5e79 --- /dev/null +++ b/shell/platform/windows/flutter_keyboard_manager_unittests.cc @@ -0,0 +1,160 @@ +// 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/windows/flutter_keyboard_manager.h" + +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" +#include "flutter/shell/platform/windows/testing/engine_embedder_api_modifier.h" +#include "gtest/gtest.h" + +namespace flutter { + +namespace { + +std::unique_ptr allocCopyString(const char* src) { + if (src == nullptr) + return nullptr; + size_t len = strlen(src); + std::unique_ptr dst = std::make_unique(len + 1); + memcpy(dst.get(), src, (len + 1) * sizeof(char)); + return dst; +} + +class TestFlutterKeyEvent : public FlutterKeyEvent { + public: + TestFlutterKeyEvent(const FlutterKeyEvent& src) { + struct_size = src.struct_size; + timestamp = src.timestamp; + kind = src.kind; + physical = src.physical; + logical = src.logical; + character_ptr = allocCopyString(src.character); + character = character_ptr.get(); + synthesized = src.synthesized; + } + + private: + std::unique_ptr character_ptr; +}; + +} // namespace + +namespace testing { + +namespace { +constexpr uint64_t kPhysicalKeyA = 0x1e; +constexpr uint64_t kPhysicalNumpad1 = 0x4f; +constexpr uint64_t kPhysicalNumLock = 0x145; + +constexpr uint64_t kLogicalKeyA = 0x41; +constexpr uint64_t kLogicalNumpad1 = 0x61; +constexpr uint64_t kLogicalNumpadEnd = 0x23; +constexpr uint64_t kLogicalNumLock = 0x90; +} + +TEST(FlutterKeyboardManager, BasicKeyPressingAndHolding) { + std::vector results; + TestFlutterKeyEvent* event; + + std::unique_ptr manager = std::make_unique( + [&results](const FlutterKeyEvent& event) { + results.emplace_back(event); + } + ); + + // On a US keyboard: + // Press Numpad1. + manager->KeyboardHook(nullptr, kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->kind, kFlutterKeyEventKindDown); + EXPECT_EQ(event->physical, 0x00070004); + EXPECT_EQ(event->logical, 0x00000061); + EXPECT_STREQ(event->character, "a"); + EXPECT_EQ(event->synthesized, false); + results.clear(); + + // Hold KeyA. + manager->KeyboardHook(nullptr, kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', true); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->kind, kFlutterKeyEventKindRepeat); + EXPECT_EQ(event->physical, 0x00070004); + EXPECT_EQ(event->logical, 0x00000061); + EXPECT_STREQ(event->character, "a"); + EXPECT_EQ(event->synthesized, false); + results.clear(); + + // Release KeyA. + manager->KeyboardHook(nullptr, kLogicalKeyA, kPhysicalKeyA, WM_KEYUP, 0, true); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->kind, kFlutterKeyEventKindUp); + EXPECT_EQ(event->physical, 0x00070004); + EXPECT_EQ(event->logical, 0x00000061); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + +} + +TEST(FlutterKeyboardManager, ToggleNumLockDuringNumpadPress) { + std::vector results; + TestFlutterKeyEvent* event; + + std::unique_ptr manager = std::make_unique( + [&results](const FlutterKeyEvent& event) { + results.emplace_back(event); + } + ); + + // On a US keyboard: + // Press NumPad1. + manager->KeyboardHook(nullptr, kLogicalNumpad1, kPhysicalNumpad1, WM_KEYDOWN, 0, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->kind, kFlutterKeyEventKindDown); + EXPECT_EQ(event->physical, 0x00070059); + EXPECT_EQ(event->logical, 0x00200000031); + // EXPECT_STREQ(event->character, "1"); // TODO + EXPECT_EQ(event->synthesized, false); + results.clear(); + + // Press NumLock. + manager->KeyboardHook(nullptr, kLogicalNumLock, kPhysicalNumLock, WM_KEYDOWN, 0, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->kind, kFlutterKeyEventKindDown); + EXPECT_EQ(event->physical, 0x00070053); + EXPECT_EQ(event->logical, 0x0000010a); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + results.clear(); + + // Release NumLock. + manager->KeyboardHook(nullptr, kLogicalNumLock, kPhysicalNumLock, WM_KEYUP, 0, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->kind, kFlutterKeyEventKindUp); + EXPECT_EQ(event->physical, 0x00070053); + EXPECT_EQ(event->logical, 0x0000010a); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + results.clear(); + + // Release NumPad1. (The logical key is now NumpadEnd) + manager->KeyboardHook(nullptr, kLogicalNumpadEnd, kPhysicalNumpad1, WM_KEYUP, 0, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->kind, kFlutterKeyEventKindUp); + EXPECT_EQ(event->physical, 0x00070059); + EXPECT_EQ(event->logical, 0x00200000031); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + results.clear(); + +} + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index a284baa6f9da9..ae16af7468942 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -268,6 +268,12 @@ void FlutterWindowsEngine::SendPointerEvent(const FlutterPointerEvent& event) { } } +void FlutterWindowsEngine::SendKeyEvent(const FlutterKeyEvent& event) { + if (engine_) { + embedder_api_.SendKeyEvent(engine_, &event); + } +} + bool FlutterWindowsEngine::SendPlatformMessage( const char* channel, const uint8_t* message, diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index 86fe2c9a20978..caf52b4fa96af 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -92,6 +92,9 @@ class FlutterWindowsEngine { // Informs the engine of an incoming pointer event. void SendPointerEvent(const FlutterPointerEvent& event); + // Informs the engine of an incoming key event. + void SendKeyEvent(const FlutterKeyEvent& event); + // Sends the given message to the engine, calling |reply| with |user_data| // when a reponse is received from the engine if they are non-null. bool SendPlatformMessage(const char* channel, diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index bcac661aa17fb..e539f5d844393 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -6,6 +6,8 @@ #include +#include "flutter/shell/platform/windows/flutter_keyboard_manager.h" + namespace flutter { FlutterWindowsView::FlutterWindowsView( @@ -35,6 +37,11 @@ void FlutterWindowsView::SetEngine( // Set up the system channel handlers. auto internal_plugin_messenger = internal_plugin_registrar_->messenger(); + keyboard_hook_handlers_.push_back( + std::make_unique( + [this](const FlutterKeyEvent& event){ + engine_->SendKeyEvent(event); + })); keyboard_hook_handlers_.push_back( std::make_unique(internal_plugin_messenger)); keyboard_hook_handlers_.push_back( @@ -92,8 +99,9 @@ void FlutterWindowsView::OnText(const std::u16string& text) { void FlutterWindowsView::OnKey(int key, int scancode, int action, - char32_t character) { - SendKey(key, scancode, action, character); + char32_t character, + bool wasDown) { + SendKey(key, scancode, action, character, wasDown); } void FlutterWindowsView::OnScroll(double x, @@ -180,9 +188,10 @@ void FlutterWindowsView::SendText(const std::u16string& text) { void FlutterWindowsView::SendKey(int key, int scancode, int action, - char32_t character) { + char32_t character, + bool wasDown) { for (const auto& handler : keyboard_hook_handlers_) { - handler->KeyboardHook(this, key, scancode, action, character); + handler->KeyboardHook(this, key, scancode, action, character, wasDown); } } diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 4863ef7b1a30f..2561659a281a8 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -89,7 +89,7 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate { void OnText(const std::u16string&) override; // |WindowBindingHandlerDelegate| - void OnKey(int key, int scancode, int action, char32_t character) override; + void OnKey(int key, int scancode, int action, char32_t character, bool wasDown) override; // |WindowBindingHandlerDelegate| void OnScroll(double x, @@ -139,7 +139,7 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate { void SendText(const std::u16string&); // Reports a raw keyboard message to Flutter engine. - void SendKey(int key, int scancode, int action, char32_t character); + void SendKey(int key, int scancode, int action, char32_t character, bool wasDown); // Reports scroll wheel events to Flutter engine. void SendScroll(double x, diff --git a/shell/platform/windows/key_event_handler.cc b/shell/platform/windows/key_event_handler.cc index 9b562f8f0944e..b7f7fae235a51 100644 --- a/shell/platform/windows/key_event_handler.cc +++ b/shell/platform/windows/key_event_handler.cc @@ -99,7 +99,8 @@ void KeyEventHandler::KeyboardHook(FlutterWindowsView* view, int key, int scancode, int action, - char32_t character) { + char32_t character, + bool wasDown) { // TODO: Translate to a cross-platform key code system rather than passing // the native key code. rapidjson::Document event(rapidjson::kObjectType); diff --git a/shell/platform/windows/key_event_handler.h b/shell/platform/windows/key_event_handler.h index 9a1b2752288e9..060f54f7fdfdb 100644 --- a/shell/platform/windows/key_event_handler.h +++ b/shell/platform/windows/key_event_handler.h @@ -32,7 +32,8 @@ class KeyEventHandler : public KeyboardHookHandler { int key, int scancode, int action, - char32_t character) override; + char32_t character, + bool wasDown) override; // |KeyboardHookHandler| void TextHook(FlutterWindowsView* window, diff --git a/shell/platform/windows/keyboard_hook_handler.h b/shell/platform/windows/keyboard_hook_handler.h index b36ad3a6d7d82..ef7c3df266d79 100644 --- a/shell/platform/windows/keyboard_hook_handler.h +++ b/shell/platform/windows/keyboard_hook_handler.h @@ -23,7 +23,8 @@ class KeyboardHookHandler { int key, int scancode, int action, - char32_t character) = 0; + char32_t character, + bool wasDown) = 0; // A function for hooking into Unicode text input. virtual void TextHook(FlutterWindowsView* view, diff --git a/shell/platform/windows/testing/mock_win32_window.h b/shell/platform/windows/testing/mock_win32_window.h index 224aaf9090510..aca86ff62282a 100644 --- a/shell/platform/windows/testing/mock_win32_window.h +++ b/shell/platform/windows/testing/mock_win32_window.h @@ -36,7 +36,7 @@ class MockWin32Window : public Win32Window { MOCK_METHOD0(OnPointerLeave, void()); MOCK_METHOD0(OnSetCursor, void()); MOCK_METHOD1(OnText, void(const std::u16string&)); - MOCK_METHOD4(OnKey, void(int, int, int, char32_t)); + MOCK_METHOD5(OnKey, void(int, int, int, char32_t, bool)); MOCK_METHOD2(OnScroll, void(double, double)); }; diff --git a/shell/platform/windows/text_input_plugin.cc b/shell/platform/windows/text_input_plugin.cc index ebfe004292644..b727bf0229742 100644 --- a/shell/platform/windows/text_input_plugin.cc +++ b/shell/platform/windows/text_input_plugin.cc @@ -56,7 +56,8 @@ void TextInputPlugin::KeyboardHook(FlutterWindowsView* view, int key, int scancode, int action, - char32_t character) { + char32_t character, + bool wasDown) { if (active_model_ == nullptr) { return; } diff --git a/shell/platform/windows/text_input_plugin.h b/shell/platform/windows/text_input_plugin.h index c0b2452ecd310..28b8c92f7119a 100644 --- a/shell/platform/windows/text_input_plugin.h +++ b/shell/platform/windows/text_input_plugin.h @@ -33,7 +33,8 @@ class TextInputPlugin : public KeyboardHookHandler { int key, int scancode, int action, - char32_t character) override; + char32_t character, + bool wasDown) override; // |KeyboardHookHandler| void TextHook(FlutterWindowsView* view, const std::u16string& text) override; diff --git a/shell/platform/windows/win32_flutter_window.cc b/shell/platform/windows/win32_flutter_window.cc index 5f4c92aa98607..1b49c88a0b82b 100644 --- a/shell/platform/windows/win32_flutter_window.cc +++ b/shell/platform/windows/win32_flutter_window.cc @@ -158,8 +158,9 @@ void Win32FlutterWindow::OnText(const std::u16string& text) { void Win32FlutterWindow::OnKey(int key, int scancode, int action, - char32_t character) { - binding_handler_delegate_->OnKey(key, scancode, action, character); + char32_t character, + bool wasDown) { + binding_handler_delegate_->OnKey(key, scancode, action, character, wasDown); } void Win32FlutterWindow::OnScroll(double delta_x, double delta_y) { diff --git a/shell/platform/windows/win32_flutter_window.h b/shell/platform/windows/win32_flutter_window.h index 6d26bcf6bffa0..3b311afa40422 100644 --- a/shell/platform/windows/win32_flutter_window.h +++ b/shell/platform/windows/win32_flutter_window.h @@ -54,7 +54,7 @@ class Win32FlutterWindow : public Win32Window, public WindowBindingHandler { void OnText(const std::u16string& text) override; // |Win32Window| - void OnKey(int key, int scancode, int action, char32_t character) override; + void OnKey(int key, int scancode, int action, char32_t character, bool wasDown) override; // |Win32Window| void OnScroll(double delta_x, double delta_y) override; diff --git a/shell/platform/windows/win32_window.cc b/shell/platform/windows/win32_window.cc index 7ed1f7c0ccd6f..09a946805f57b 100644 --- a/shell/platform/windows/win32_window.cc +++ b/shell/platform/windows/win32_window.cc @@ -241,8 +241,9 @@ Win32Window::HandleMessage(UINT const message, // WM_CHAR. In order to send the full key press information, the keycode // is persisted in keycode_for_char_message_ obtained from WM_KEYDOWN. if (keycode_for_char_message_ != 0) { - const unsigned int scancode = (lparam >> 16) & 0xff; - OnKey(keycode_for_char_message_, scancode, WM_KEYDOWN, code_point); + const unsigned int scancode = (lparam >> 16) & 0x1ff; + const bool wasDown = lparam & 0x40000000; + OnKey(keycode_for_char_message_, scancode, WM_KEYDOWN, code_point, wasDown); keycode_for_char_message_ = 0; } break; @@ -262,13 +263,14 @@ Win32Window::HandleMessage(UINT const message, break; } unsigned int keyCode(wparam); - const unsigned int scancode = (lparam >> 16) & 0xff; + const unsigned int scancode = (lparam >> 16) & 0x1ff; // If the key is a modifier, get its side. if (keyCode == VK_SHIFT || keyCode == VK_MENU || keyCode == VK_CONTROL) { keyCode = MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX); } const int action = is_keydown_message ? WM_KEYDOWN : WM_KEYUP; - OnKey(keyCode, scancode, action, 0); + const bool wasDown = lparam & 0x40000000; + OnKey(keyCode, scancode, action, 0, wasDown); break; } diff --git a/shell/platform/windows/win32_window.h b/shell/platform/windows/win32_window.h index 453bc0e90480f..ef2176a95799b 100644 --- a/shell/platform/windows/win32_window.h +++ b/shell/platform/windows/win32_window.h @@ -92,7 +92,7 @@ class Win32Window { virtual void OnText(const std::u16string& text) = 0; // Called when raw keyboard input occurs. - virtual void OnKey(int key, int scancode, int action, char32_t character) = 0; + virtual void OnKey(int key, int scancode, int action, char32_t character, bool wasDown) = 0; // Called when mouse scrollwheel input occurs. virtual void OnScroll(double delta_x, double delta_y) = 0; diff --git a/shell/platform/windows/window_binding_handler_delegate.h b/shell/platform/windows/window_binding_handler_delegate.h index 30bc5b3111bbe..334614c9e09af 100644 --- a/shell/platform/windows/window_binding_handler_delegate.h +++ b/shell/platform/windows/window_binding_handler_delegate.h @@ -41,7 +41,7 @@ class WindowBindingHandlerDelegate { // Notifies delegate that backing window size has received key press. // Typically called by currently configured WindowBindingHandler - virtual void OnKey(int key, int scancode, int action, char32_t character) = 0; + virtual void OnKey(int key, int scancode, int action, char32_t character, bool wasDown) = 0; // Notifies delegate that backing window size has recevied scroll. // Typically called by currently configured WindowBindingHandler From f93d22c7813242dd95153191e06a723cdfae314e Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 14:56:23 -0800 Subject: [PATCH 02/82] Split out web --- lib/web_ui/lib/src/engine.dart | 2 + lib/web_ui/lib/src/engine/dom_renderer.dart | 1 + lib/web_ui/lib/src/engine/key_map.dart | 706 +++++++++++++ .../lib/src/engine/keyboard_binding.dart | 489 +++++++++ .../lib/src/engine/platform_dispatcher.dart | 26 +- lib/web_ui/lib/src/ui/key.dart | 66 ++ .../lib/src/ui/platform_dispatcher.dart | 6 +- lib/web_ui/lib/src/ui/window.dart | 5 + lib/web_ui/lib/ui.dart | 1 + lib/web_ui/test/keyboard_converter_test.dart | 927 ++++++++++++++++++ 10 files changed, 2227 insertions(+), 2 deletions(-) create mode 100644 lib/web_ui/lib/src/engine/key_map.dart create mode 100644 lib/web_ui/lib/src/engine/keyboard_binding.dart create mode 100644 lib/web_ui/lib/src/ui/key.dart create mode 100644 lib/web_ui/test/keyboard_converter_test.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 42040eb1afa56..d8e42bafd00cb 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -96,7 +96,9 @@ part 'engine/html/surface.dart'; part 'engine/html/surface_stats.dart'; part 'engine/html/transform.dart'; part 'engine/html_image_codec.dart'; +part 'engine/keyboard_binding.dart'; part 'engine/keyboard.dart'; +part 'engine/key_map.dart'; part 'engine/mouse_cursor.dart'; part 'engine/onscreen_logging.dart'; part 'engine/picture.dart'; diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index b14d007ead10b..34b594b668721 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -421,6 +421,7 @@ flt-glass-pane * { glassPaneElement.insertBefore(_accesibilityPlaceholder, _sceneHostElement); PointerBinding.initInstance(glassPaneElement); + KeyboardBinding.initInstance(glassPaneElement); // Hide the DOM nodes used to render the scene from accessibility, because // the accessibility tree is built from the SemanticsNode tree as a parallel diff --git a/lib/web_ui/lib/src/engine/key_map.dart b/lib/web_ui/lib/src/engine/key_map.dart new file mode 100644 index 0000000000000..d3970549c7ca2 --- /dev/null +++ b/lib/web_ui/lib/src/engine/key_map.dart @@ -0,0 +1,706 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT +// This file is generated by dev/tools/gen_keycodes/bin/gen_keycodes.dart and +// should not be edited directly. +// +// Edit the template dev/tools/gen_keycodes/data/web_key_map_dart.tmpl instead. +// See dev/tools/gen_keycodes/README.md for more information. + +// @dart = 2.12 +part of engine; + +/// Maps Web KeyboardEvent codes to the matching LogicalKeyboardKey id. +const Map kWebToLogicalKey = { + 'None': 0x0000000000, + 'Unidentified': 0x0000000001, + 'Backspace': 0x0000000008, + 'Tab': 0x0000000009, + 'Enter': 0x000000000d, + 'Escape': 0x000000001b, + 'Space': 0x0000000020, + 'Exclamation': 0x0000000021, + 'Quote': 0x0000000022, + 'NumberSign': 0x0000000023, + 'Dollar': 0x0000000024, + 'Ampersand': 0x0000000026, + 'QuoteSingle': 0x0000000027, + 'ParenthesisLeft': 0x0000000028, + 'ParenthesisRight': 0x0000000029, + 'Asterisk': 0x000000002a, + 'Add': 0x000000002b, + 'Comma': 0x000000002c, + 'Minus': 0x000000002d, + 'Period': 0x000000002e, + 'Slash': 0x000000002f, + 'Digit0': 0x0000000030, + 'Digit1': 0x0000000031, + 'Digit2': 0x0000000032, + 'Digit3': 0x0000000033, + 'Digit4': 0x0000000034, + 'Digit5': 0x0000000035, + 'Digit6': 0x0000000036, + 'Digit7': 0x0000000037, + 'Digit8': 0x0000000038, + 'Digit9': 0x0000000039, + 'Colon': 0x000000003a, + 'Semicolon': 0x000000003b, + 'Less': 0x000000003c, + 'Equal': 0x000000003d, + 'Greater': 0x000000003e, + 'Question': 0x000000003f, + 'At': 0x0000000040, + 'BracketLeft': 0x000000005b, + 'Backslash': 0x000000005c, + 'BracketRight': 0x000000005d, + 'Caret': 0x000000005e, + 'Underscore': 0x000000005f, + 'Backquote': 0x0000000060, + 'KeyA': 0x0000000061, + 'KeyB': 0x0000000062, + 'KeyC': 0x0000000063, + 'KeyD': 0x0000000064, + 'KeyE': 0x0000000065, + 'KeyF': 0x0000000066, + 'KeyG': 0x0000000067, + 'KeyH': 0x0000000068, + 'KeyI': 0x0000000069, + 'KeyJ': 0x000000006a, + 'KeyK': 0x000000006b, + 'KeyL': 0x000000006c, + 'KeyM': 0x000000006d, + 'KeyN': 0x000000006e, + 'KeyO': 0x000000006f, + 'KeyP': 0x0000000070, + 'KeyQ': 0x0000000071, + 'KeyR': 0x0000000072, + 'KeyS': 0x0000000073, + 'KeyT': 0x0000000074, + 'KeyU': 0x0000000075, + 'KeyV': 0x0000000076, + 'KeyW': 0x0000000077, + 'KeyX': 0x0000000078, + 'KeyY': 0x0000000079, + 'KeyZ': 0x000000007a, + 'BraceLeft': 0x000000007b, + 'Bar': 0x000000007c, + 'BraceRight': 0x000000007d, + 'Tilde': 0x000000007e, + 'Delete': 0x000000007f, + 'Accel': 0x0000000101, + 'AltGraph': 0x0000000103, + 'CapsLock': 0x0000000104, + 'Fn': 0x0000000106, + 'FnLock': 0x0000000107, + 'Hyper': 0x0000000108, + 'NumLock': 0x000000010a, + 'ScrollLock': 0x000000010c, + 'Super': 0x000000010e, + 'Symbol': 0x000000010f, + 'SymbolLock': 0x0000000110, + 'ShiftLevel5': 0x0000000111, + 'AltGraphLatch': 0x0000000112, + 'ArrowDown': 0x0000000301, + 'ArrowLeft': 0x0000000302, + 'ArrowRight': 0x0000000303, + 'ArrowUp': 0x0000000304, + 'End': 0x0000000305, + 'Home': 0x0000000306, + 'PageDown': 0x0000000307, + 'PageUp': 0x0000000308, + 'Clear': 0x0000000401, + 'Copy': 0x0000000402, + 'CrSel': 0x0000000403, + 'Cut': 0x0000000404, + 'EraseEof': 0x0000000405, + 'ExSel': 0x0000000406, + 'Insert': 0x0000000407, + 'Paste': 0x0000000408, + 'Redo': 0x0000000409, + 'Undo': 0x000000040a, + 'Accept': 0x0000000501, + 'Again': 0x0000000502, + 'Attn': 0x0000000503, + 'Cancel': 0x0000000504, + 'ContextMenu': 0x0000000505, + 'Execute': 0x0000000506, + 'Find': 0x0000000507, + 'Help': 0x0000000508, + 'Pause': 0x0000000509, + 'Play': 0x000000050a, + 'Props': 0x000000050b, + 'Select': 0x000000050c, + 'ZoomIn': 0x000000050d, + 'ZoomOut': 0x000000050e, + 'BrightnessDown': 0x0000000601, + 'BrightnessUp': 0x0000000602, + 'Camera': 0x0000000603, + 'Eject': 0x0000000604, + 'LogOff': 0x0000000605, + 'Power': 0x0000000606, + 'PowerOff': 0x0000000607, + 'PrintScreen': 0x0000000608, + 'Hibernate': 0x0000000609, + 'Standby': 0x000000060a, + 'WakeUp': 0x000000060b, + 'AllCandidates': 0x0000000701, + 'Alphanumeric': 0x0000000702, + 'CodeInput': 0x0000000703, + 'Compose': 0x0000000704, + 'Convert': 0x0000000705, + 'FinalMode': 0x0000000706, + 'GroupFirst': 0x0000000707, + 'GroupLast': 0x0000000708, + 'GroupNext': 0x0000000709, + 'GroupPrevious': 0x000000070a, + 'ModeChange': 0x000000070b, + 'NextCandidate': 0x000000070c, + 'NonConvert': 0x000000070d, + 'PreviousCandidate': 0x000000070e, + 'Process': 0x000000070f, + 'SingleCandidate': 0x0000000710, + 'HangulMode': 0x0000000711, + 'HanjaMode': 0x0000000712, + 'JunjaMode': 0x0000000713, + 'Eisu': 0x0000000714, + 'Hankaku': 0x0000000715, + 'Hiragana': 0x0000000716, + 'HiraganaKatakana': 0x0000000717, + 'KanaMode': 0x0000000718, + 'KanjiMode': 0x0000000719, + 'Katakana': 0x000000071a, + 'Romaji': 0x000000071b, + 'Zenkaku': 0x000000071c, + 'ZenkakuHankaku': 0x000000071d, + 'F1': 0x0000000801, + 'F2': 0x0000000802, + 'F3': 0x0000000803, + 'F4': 0x0000000804, + 'F5': 0x0000000805, + 'F6': 0x0000000806, + 'F7': 0x0000000807, + 'F8': 0x0000000808, + 'F9': 0x0000000809, + 'F10': 0x000000080a, + 'F11': 0x000000080b, + 'F12': 0x000000080c, + 'F13': 0x000000080d, + 'F14': 0x000000080e, + 'F15': 0x000000080f, + 'F16': 0x0000000810, + 'F17': 0x0000000811, + 'F18': 0x0000000812, + 'F19': 0x0000000813, + 'F20': 0x0000000814, + 'F21': 0x0000000815, + 'F22': 0x0000000816, + 'F23': 0x0000000817, + 'F24': 0x0000000818, + 'Soft1': 0x0000000901, + 'Soft2': 0x0000000902, + 'Soft3': 0x0000000903, + 'Soft4': 0x0000000904, + 'Soft5': 0x0000000905, + 'Soft6': 0x0000000906, + 'Soft7': 0x0000000907, + 'Soft8': 0x0000000908, + 'Close': 0x0000000a01, + 'MailForward': 0x0000000a02, + 'MailReply': 0x0000000a03, + 'MailSend': 0x0000000a04, + 'MediaPlayPause': 0x0000000a05, + 'MediaStop': 0x0000000a07, + 'MediaTrackNext': 0x0000000a08, + 'MediaTrackPrevious': 0x0000000a09, + 'New': 0x0000000a0a, + 'Open': 0x0000000a0b, + 'Print': 0x0000000a0c, + 'Save': 0x0000000a0d, + 'SpellCheck': 0x0000000a0e, + 'AudioVolumeDown': 0x0000000a0f, + 'AudioVolumeUp': 0x0000000a10, + 'AudioVolumeMute': 0x0000000a11, + 'LaunchApplication2': 0x0000000b01, + 'LaunchCalendar': 0x0000000b02, + 'LaunchMail': 0x0000000b03, + 'LaunchMediaPlayer': 0x0000000b04, + 'LaunchMusicPlayer': 0x0000000b05, + 'LaunchApplication1': 0x0000000b06, + 'LaunchScreenSaver': 0x0000000b07, + 'LaunchSpreadsheet': 0x0000000b08, + 'LaunchWebBrowser': 0x0000000b09, + 'LaunchWebCam': 0x0000000b0a, + 'LaunchWordProcessor': 0x0000000b0b, + 'LaunchContacts': 0x0000000b0c, + 'LaunchPhone': 0x0000000b0d, + 'LaunchAssistant': 0x0000000b0e, + 'LaunchControlPanel': 0x0000000b0f, + 'BrowserBack': 0x0000000c01, + 'BrowserFavorites': 0x0000000c02, + 'BrowserForward': 0x0000000c03, + 'BrowserHome': 0x0000000c04, + 'BrowserRefresh': 0x0000000c05, + 'BrowserSearch': 0x0000000c06, + 'BrowserStop': 0x0000000c07, + 'AudioBalanceLeft': 0x0000000d01, + 'AudioBalanceRight': 0x0000000d02, + 'AudioBassBoostDown': 0x0000000d03, + 'AudioBassBoostUp': 0x0000000d04, + 'AudioFaderFront': 0x0000000d05, + 'AudioFaderRear': 0x0000000d06, + 'AudioSurroundModeNext': 0x0000000d07, + 'AVRInput': 0x0000000d08, + 'AVRPower': 0x0000000d09, + 'ChannelDown': 0x0000000d0a, + 'ChannelUp': 0x0000000d0b, + 'ColorF0Red': 0x0000000d0c, + 'ColorF1Green': 0x0000000d0d, + 'ColorF2Yellow': 0x0000000d0e, + 'ColorF3Blue': 0x0000000d0f, + 'ColorF4Grey': 0x0000000d10, + 'ColorF5Brown': 0x0000000d11, + 'ClosedCaptionToggle': 0x0000000d12, + 'Dimmer': 0x0000000d13, + 'DisplaySwap': 0x0000000d14, + 'Exit': 0x0000000d15, + 'FavoriteClear0': 0x0000000d16, + 'FavoriteClear1': 0x0000000d17, + 'FavoriteClear2': 0x0000000d18, + 'FavoriteClear3': 0x0000000d19, + 'FavoriteRecall0': 0x0000000d1a, + 'FavoriteRecall1': 0x0000000d1b, + 'FavoriteRecall2': 0x0000000d1c, + 'FavoriteRecall3': 0x0000000d1d, + 'FavoriteStore0': 0x0000000d1e, + 'FavoriteStore1': 0x0000000d1f, + 'FavoriteStore2': 0x0000000d20, + 'FavoriteStore3': 0x0000000d21, + 'Guide': 0x0000000d22, + 'GuideNextDay': 0x0000000d23, + 'GuidePreviousDay': 0x0000000d24, + 'Info': 0x0000000d25, + 'InstantReplay': 0x0000000d26, + 'Link': 0x0000000d27, + 'ListProgram': 0x0000000d28, + 'LiveContent': 0x0000000d29, + 'Lock': 0x0000000d2a, + 'MediaApps': 0x0000000d2b, + 'MediaFastForward': 0x0000000d2c, + 'MediaLast': 0x0000000d2d, + 'MediaPause': 0x0000000d2e, + 'MediaPlay': 0x0000000d2f, + 'MediaRecord': 0x0000000d30, + 'MediaRewind': 0x0000000d31, + 'MediaSkip': 0x0000000d32, + 'NextFavoriteChannel': 0x0000000d33, + 'NextUserProfile': 0x0000000d34, + 'OnDemand': 0x0000000d35, + 'PinPDown': 0x0000000d36, + 'PinPMove': 0x0000000d37, + 'PinPToggle': 0x0000000d38, + 'PinPUp': 0x0000000d39, + 'PlaySpeedDown': 0x0000000d3a, + 'PlaySpeedReset': 0x0000000d3b, + 'PlaySpeedUp': 0x0000000d3c, + 'RandomToggle': 0x0000000d3d, + 'RcLowBattery': 0x0000000d3e, + 'RecordSpeedNext': 0x0000000d3f, + 'RfBypass': 0x0000000d40, + 'ScanChannelsToggle': 0x0000000d41, + 'ScreenModeNext': 0x0000000d42, + 'Settings': 0x0000000d43, + 'SplitScreenToggle': 0x0000000d44, + 'STBInput': 0x0000000d45, + 'STBPower': 0x0000000d46, + 'Subtitle': 0x0000000d47, + 'Teletext': 0x0000000d48, + 'TV': 0x0000000d49, + 'TVInput': 0x0000000d4a, + 'TVPower': 0x0000000d4b, + 'VideoModeNext': 0x0000000d4c, + 'Wink': 0x0000000d4d, + 'ZoomToggle': 0x0000000d4e, + 'DVR': 0x0000000d4f, + 'MediaAudioTrack': 0x0000000d50, + 'MediaSkipBackward': 0x0000000d51, + 'MediaSkipForward': 0x0000000d52, + 'MediaStepBackward': 0x0000000d53, + 'MediaStepForward': 0x0000000d54, + 'MediaTopMenu': 0x0000000d55, + 'NavigateIn': 0x0000000d56, + 'NavigateNext': 0x0000000d57, + 'NavigateOut': 0x0000000d58, + 'NavigatePrevious': 0x0000000d59, + 'Pairing': 0x0000000d5a, + 'MediaClose': 0x0000000d5b, + 'AudioBassBoostToggle': 0x0000000e02, + 'AudioTrebleDown': 0x0000000e04, + 'AudioTrebleUp': 0x0000000e05, + 'MicrophoneToggle': 0x0000000e06, + 'MicrophoneVolumeDown': 0x0000000e07, + 'MicrophoneVolumeUp': 0x0000000e08, + 'MicrophoneVolumeMute': 0x0000000e09, + 'SpeechCorrectionList': 0x0000000f01, + 'SpeechInputToggle': 0x0000000f02, + 'AppSwitch': 0x0000001001, + 'Call': 0x0000001002, + 'CameraFocus': 0x0000001003, + 'EndCall': 0x0000001004, + 'GoBack': 0x0000001005, + 'GoHome': 0x0000001006, + 'HeadsetHook': 0x0000001007, + 'LastNumberRedial': 0x0000001008, + 'Notification': 0x0000001009, + 'MannerMode': 0x000000100a, + 'VoiceDial': 0x000000100b, + 'TV3DMode': 0x0000001101, + 'TVAntennaCable': 0x0000001102, + 'TVAudioDescription': 0x0000001103, + 'TVAudioDescriptionMixDown': 0x0000001104, + 'TVAudioDescriptionMixUp': 0x0000001105, + 'TVContentsMenu': 0x0000001106, + 'TVDataService': 0x0000001107, + 'TVInputComponent1': 0x0000001108, + 'TVInputComponent2': 0x0000001109, + 'TVInputComposite1': 0x000000110a, + 'TVInputComposite2': 0x000000110b, + 'TVInputHDMI1': 0x000000110c, + 'TVInputHDMI2': 0x000000110d, + 'TVInputHDMI3': 0x000000110e, + 'TVInputHDMI4': 0x000000110f, + 'TVInputVGA1': 0x0000001110, + 'TVMediaContext': 0x0000001111, + 'TVNetwork': 0x0000001112, + 'TVNumberEntry': 0x0000001113, + 'TVRadioService': 0x0000001114, + 'TVSatellite': 0x0000001115, + 'TVSatelliteBS': 0x0000001116, + 'TVSatelliteCS': 0x0000001117, + 'TVSatelliteToggle': 0x0000001118, + 'TVTerrestrialAnalog': 0x0000001119, + 'TVTerrestrialDigital': 0x000000111a, + 'TVTimer': 0x000000111b, + 'Key11': 0x0000001201, + 'Key12': 0x0000001202, + 'GameButton1': 0x000005ff01, + 'GameButton2': 0x000005ff02, + 'GameButton3': 0x000005ff03, + 'GameButton4': 0x000005ff04, + 'GameButton5': 0x000005ff05, + 'GameButton6': 0x000005ff06, + 'GameButton7': 0x000005ff07, + 'GameButton8': 0x000005ff08, + 'GameButton9': 0x000005ff09, + 'GameButton10': 0x000005ff0a, + 'GameButton11': 0x000005ff0b, + 'GameButton12': 0x000005ff0c, + 'GameButton13': 0x000005ff0d, + 'GameButton14': 0x000005ff0e, + 'GameButton15': 0x000005ff0f, + 'GameButton16': 0x000005ff10, + 'GameButtonA': 0x000005ff11, + 'GameButtonB': 0x000005ff12, + 'GameButtonC': 0x000005ff13, + 'GameButtonLeft1': 0x000005ff14, + 'GameButtonLeft2': 0x000005ff15, + 'GameButtonMode': 0x000005ff16, + 'GameButtonRight1': 0x000005ff17, + 'GameButtonRight2': 0x000005ff18, + 'GameButtonSelect': 0x000005ff19, + 'GameButtonStart': 0x000005ff1a, + 'GameButtonThumbLeft': 0x000005ff1b, + 'GameButtonThumbRight': 0x000005ff1c, + 'GameButtonX': 0x000005ff1d, + 'GameButtonY': 0x000005ff1e, + 'GameButtonZ': 0x000005ff1f, + 'Suspend': 0x0100000014, + 'Resume': 0x0100000015, + 'Sleep': 0x0100010082, + 'IntlBackslash': 0x0100070064, + 'IntlRo': 0x0100070087, + 'IntlYen': 0x0100070089, + 'Lang1': 0x0100070090, + 'Lang2': 0x0100070091, + 'Lang3': 0x0100070092, + 'Lang4': 0x0100070093, + 'Lang5': 0x0100070094, + 'Abort': 0x010007009b, +}; + +/// Maps Web KeyboardEvent codes to the matching PhysicalKeyboardKey USB HID code. +const Map kWebToPhysicalKey = { + 'None': 0x00000000, + 'Hyper': 0x00000010, + 'Super': 0x00000011, + 'FnLock': 0x00000013, + 'Suspend': 0x00000014, + 'Resume': 0x00000015, + 'Turbo': 0x00000016, + 'PrivacyScreenToggle': 0x00000017, + 'Sleep': 0x00010082, + 'WakeUp': 0x00010083, + 'DisplayToggleIntExt': 0x000100b5, + 'KeyA': 0x00070004, + 'KeyB': 0x00070005, + 'KeyC': 0x00070006, + 'KeyD': 0x00070007, + 'KeyE': 0x00070008, + 'KeyF': 0x00070009, + 'KeyG': 0x0007000a, + 'KeyH': 0x0007000b, + 'KeyI': 0x0007000c, + 'KeyJ': 0x0007000d, + 'KeyK': 0x0007000e, + 'KeyL': 0x0007000f, + 'KeyM': 0x00070010, + 'KeyN': 0x00070011, + 'KeyO': 0x00070012, + 'KeyP': 0x00070013, + 'KeyQ': 0x00070014, + 'KeyR': 0x00070015, + 'KeyS': 0x00070016, + 'KeyT': 0x00070017, + 'KeyU': 0x00070018, + 'KeyV': 0x00070019, + 'KeyW': 0x0007001a, + 'KeyX': 0x0007001b, + 'KeyY': 0x0007001c, + 'KeyZ': 0x0007001d, + 'Digit1': 0x0007001e, + 'Digit2': 0x0007001f, + 'Digit3': 0x00070020, + 'Digit4': 0x00070021, + 'Digit5': 0x00070022, + 'Digit6': 0x00070023, + 'Digit7': 0x00070024, + 'Digit8': 0x00070025, + 'Digit9': 0x00070026, + 'Digit0': 0x00070027, + 'Enter': 0x00070028, + 'Escape': 0x00070029, + 'Backspace': 0x0007002a, + 'Tab': 0x0007002b, + 'Space': 0x0007002c, + 'Minus': 0x0007002d, + 'Equal': 0x0007002e, + 'BracketLeft': 0x0007002f, + 'BracketRight': 0x00070030, + 'Backslash': 0x00070031, + 'Semicolon': 0x00070033, + 'Quote': 0x00070034, + 'Backquote': 0x00070035, + 'Comma': 0x00070036, + 'Period': 0x00070037, + 'Slash': 0x00070038, + 'CapsLock': 0x00070039, + 'F1': 0x0007003a, + 'F2': 0x0007003b, + 'F3': 0x0007003c, + 'F4': 0x0007003d, + 'F5': 0x0007003e, + 'F6': 0x0007003f, + 'F7': 0x00070040, + 'F8': 0x00070041, + 'F9': 0x00070042, + 'F10': 0x00070043, + 'F11': 0x00070044, + 'F12': 0x00070045, + 'PrintScreen': 0x00070046, + 'ScrollLock': 0x00070047, + 'Pause': 0x00070048, + 'Insert': 0x00070049, + 'Home': 0x0007004a, + 'PageUp': 0x0007004b, + 'Delete': 0x0007004c, + 'End': 0x0007004d, + 'PageDown': 0x0007004e, + 'ArrowRight': 0x0007004f, + 'ArrowLeft': 0x00070050, + 'ArrowDown': 0x00070051, + 'ArrowUp': 0x00070052, + 'NumLock': 0x00070053, + 'NumpadDivide': 0x00070054, + 'NumpadMultiply': 0x00070055, + 'NumpadSubtract': 0x00070056, + 'NumpadAdd': 0x00070057, + 'NumpadEnter': 0x00070058, + 'Numpad1': 0x00070059, + 'Numpad2': 0x0007005a, + 'Numpad3': 0x0007005b, + 'Numpad4': 0x0007005c, + 'Numpad5': 0x0007005d, + 'Numpad6': 0x0007005e, + 'Numpad7': 0x0007005f, + 'Numpad8': 0x00070060, + 'Numpad9': 0x00070061, + 'Numpad0': 0x00070062, + 'NumpadDecimal': 0x00070063, + 'IntlBackslash': 0x00070064, + 'ContextMenu': 0x00070065, + 'Power': 0x00070066, + 'NumpadEqual': 0x00070067, + 'F13': 0x00070068, + 'F14': 0x00070069, + 'F15': 0x0007006a, + 'F16': 0x0007006b, + 'F17': 0x0007006c, + 'F18': 0x0007006d, + 'F19': 0x0007006e, + 'F20': 0x0007006f, + 'F21': 0x00070070, + 'F22': 0x00070071, + 'F23': 0x00070072, + 'F24': 0x00070073, + 'Open': 0x00070074, + 'Help': 0x00070075, + 'Select': 0x00070077, + 'Again': 0x00070079, + 'Undo': 0x0007007a, + 'Cut': 0x0007007b, + 'Copy': 0x0007007c, + 'Paste': 0x0007007d, + 'Find': 0x0007007e, + 'AudioVolumeMute': 0x0007007f, + 'AudioVolumeUp': 0x00070080, + 'AudioVolumeDown': 0x00070081, + 'NumpadComma': 0x00070085, + 'IntlRo': 0x00070087, + 'KanaMode': 0x00070088, + 'IntlYen': 0x00070089, + 'Convert': 0x0007008a, + 'NonConvert': 0x0007008b, + 'Lang1': 0x00070090, + 'Lang2': 0x00070091, + 'Lang3': 0x00070092, + 'Lang4': 0x00070093, + 'Lang5': 0x00070094, + 'Abort': 0x0007009b, + 'Props': 0x000700a3, + 'NumpadParenLeft': 0x000700b6, + 'NumpadParenRight': 0x000700b7, + 'NumpadBackspace': 0x000700bb, + 'NumpadMemoryStore': 0x000700d0, + 'NumpadMemoryRecall': 0x000700d1, + 'NumpadMemoryClear': 0x000700d2, + 'NumpadMemoryAdd': 0x000700d3, + 'NumpadMemorySubtract': 0x000700d4, + 'NumpadClear': 0x000700d8, + 'NumpadClearEntry': 0x000700d9, + 'ControlLeft': 0x000700e0, + 'ShiftLeft': 0x000700e1, + 'AltLeft': 0x000700e2, + 'MetaLeft': 0x000700e3, + 'ControlRight': 0x000700e4, + 'ShiftRight': 0x000700e5, + 'AltRight': 0x000700e6, + 'MetaRight': 0x000700e7, + 'BrightnessUp': 0x000c006f, + 'BrightnessDown': 0x000c0070, + 'MediaPlay': 0x000c00b0, + 'MediaPause': 0x000c00b1, + 'MediaRecord': 0x000c00b2, + 'MediaFastForward': 0x000c00b3, + 'MediaRewind': 0x000c00b4, + 'MediaTrackNext': 0x000c00b5, + 'MediaTrackPrevious': 0x000c00b6, + 'MediaStop': 0x000c00b7, + 'Eject': 0x000c00b8, + 'MediaPlayPause': 0x000c00cd, + 'MediaSelect': 0x000c0183, + 'LaunchMail': 0x000c018a, + 'LaunchApp2': 0x000c0192, + 'LaunchApp1': 0x000c0194, + 'LaunchControlPanel': 0x000c019f, + 'SelectTask': 0x000c01a2, + 'LaunchScreenSaver': 0x000c01b1, + 'LaunchAssistant': 0x000c01cb, + 'BrowserSearch': 0x000c0221, + 'BrowserHome': 0x000c0223, + 'BrowserBack': 0x000c0224, + 'BrowserForward': 0x000c0225, + 'BrowserStop': 0x000c0226, + 'BrowserRefresh': 0x000c0227, + 'BrowserFavorites': 0x000c022a, + 'ZoomToggle': 0x000c0232, + 'MailReply': 0x000c0289, + 'MailForward': 0x000c028b, + 'MailSend': 0x000c028c, + 'KeyboardLayoutSelect': 0x000c029d, + 'ShowAllWindows': 0x000c029f, + 'GameButton1': 0x0005ff01, + 'GameButton2': 0x0005ff02, + 'GameButton3': 0x0005ff03, + 'GameButton4': 0x0005ff04, + 'GameButton5': 0x0005ff05, + 'GameButton6': 0x0005ff06, + 'GameButton7': 0x0005ff07, + 'GameButton8': 0x0005ff08, + 'GameButton9': 0x0005ff09, + 'GameButton10': 0x0005ff0a, + 'GameButton11': 0x0005ff0b, + 'GameButton12': 0x0005ff0c, + 'GameButton13': 0x0005ff0d, + 'GameButton14': 0x0005ff0e, + 'GameButton15': 0x0005ff0f, + 'GameButton16': 0x0005ff10, + 'GameButtonA': 0x0005ff11, + 'GameButtonB': 0x0005ff12, + 'GameButtonC': 0x0005ff13, + 'GameButtonLeft1': 0x0005ff14, + 'GameButtonLeft2': 0x0005ff15, + 'GameButtonMode': 0x0005ff16, + 'GameButtonRight1': 0x0005ff17, + 'GameButtonRight2': 0x0005ff18, + 'GameButtonSelect': 0x0005ff19, + 'GameButtonStart': 0x0005ff1a, + 'GameButtonThumbLeft': 0x0005ff1b, + 'GameButtonThumbRight': 0x0005ff1c, + 'GameButtonX': 0x0005ff1d, + 'GameButtonY': 0x0005ff1e, + 'GameButtonZ': 0x0005ff1f, + 'Fn': 0x00000012, +}; + +/// Maps Web KeyboardEvent keys to Flutter logical IDs that depend on locations. +/// +/// `KeyboardEvent.location` is defined as: +/// +/// * 0: Standard +/// * 1: Left +/// * 2: Right +/// * 3: Numpad +const Map> kWebLogicalLocationMap = >{ + '0': [0x0000000030, null, null, 0x0200000030], + '1': [0x0000000031, null, null, 0x0200000031], + '2': [0x0000000032, null, null, 0x0200000032], + '3': [0x0000000033, null, null, 0x0200000033], + '4': [0x0000000034, null, null, 0x0200000034], + '5': [0x0000000035, null, null, 0x0200000035], + '6': [0x0000000036, null, null, 0x0200000036], + '7': [0x0000000037, null, null, 0x0200000037], + '8': [0x0000000038, null, null, 0x0200000038], + '9': [0x0000000039, null, null, 0x0200000039], + '.': [0x000000002e, null, null, 0x020000002e], + 'Insert': [0x0000000407, null, null, 0x0200000030], + 'End': [0x0000000305, null, null, 0x0200000031], + 'ArrowDown': [0x0000000301, null, null, 0x0200000032], + 'PageDown': [0x0000000307, null, null, 0x0200000033], + 'ArrowLeft': [0x0000000302, null, null, 0x0200000034], + 'Clear': [0x0000000401, null, null, 0x0200000035], + 'ArrowRight': [0x0000000303, null, null, 0x0200000036], + 'Home': [0x0000000306, null, null, 0x0200000037], + 'ArrowUp': [0x0000000304, null, null, 0x0200000038], + 'PageUp': [0x0000000308, null, null, 0x0200000039], + 'Delete': [0x000000007f, null, null, 0x020000002e], + '/': [0x000000002f, null, null, 0x020000002f], + '*': [0x000000002a, null, null, 0x020000002a], + '-': [0x000000002d, null, null, 0x020000002d], + '+': [0x000000002b, null, null, 0x020000002b], + 'Enter': [0x000000000d, null, null, 0x020000000d], + 'Shift': [null, 0x030000010d, 0x040000010d, null], + 'Control': [null, 0x0300000105, 0x0400000105, null], + 'Alt': [null, 0x0300000102, 0x0400000102, null], + 'Meta': [null, 0x0300000109, 0x0400000109, null], +}; diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart new file mode 100644 index 0000000000000..c0f734b10bebd --- /dev/null +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -0,0 +1,489 @@ +// 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. + +// @dart = 2.12 +part of engine; + +typedef VoidCallback = void Function(); +typedef ValueGetter = T Function(); +typedef _ModifierGetter = bool Function(FlutterHtmlKeyboardEvent event); + +// Set this flag to true to see all the fired events in the console. +const bool _debugLogKeyEvents = false; + +const int _kLogicalAltLeft = 0x0300000102; +const int _kLogicalAltRight = 0x0400000102; +const int _kLogicalControlLeft = 0x0300000105; +const int _kLogicalControlRight = 0x0400000105; +const int _kLogicalShiftLeft = 0x030000010d; +const int _kLogicalShiftRight = 0x040000010d; +const int _kLogicalMetaLeft = 0x0300000109; +const int _kLogicalMetaRight = 0x0400000109; +// Map logical keys for modifier keys to the functions that can get their +// modifier flag out of an event. +final Map _kLogicalKeyToModifierGetter = { + _kLogicalAltLeft: (FlutterHtmlKeyboardEvent event) => event.altKey, + _kLogicalAltRight: (FlutterHtmlKeyboardEvent event) => event.altKey, + _kLogicalControlLeft: (FlutterHtmlKeyboardEvent event) => event.ctrlKey, + _kLogicalControlRight: (FlutterHtmlKeyboardEvent event) => event.ctrlKey, + _kLogicalShiftLeft: (FlutterHtmlKeyboardEvent event) => event.shiftKey, + _kLogicalShiftRight: (FlutterHtmlKeyboardEvent event) => event.shiftKey, + _kLogicalMetaLeft: (FlutterHtmlKeyboardEvent event) => event.metaKey, + _kLogicalMetaRight: (FlutterHtmlKeyboardEvent event) => event.metaKey, +}; + +// After a keydown is received, this is the duration we wait for a repeat event +// before we decide to synthesize a keyup event. +// +// On Linux and Windows, the typical ranges for keyboard repeat delay go up to +// 1000ms. On Mac, the range goes up to 2000ms. +const Duration _kKeydownCancelDurationNormal = Duration(milliseconds: 1000); +const Duration _kKeydownCancelDurationMacOs = Duration(milliseconds: 2000); + +late final int _kCharLowerA = 'a'.codeUnitAt(0); +late final int _kCharLowerZ = 'z'.codeUnitAt(0); +late final int _kCharUpperA = 'A'.codeUnitAt(0); +late final int _kCharUpperZ = 'Z'.codeUnitAt(0); +bool isAlphabet(int charCode) { + return (charCode >= _kCharLowerA && charCode <= _kCharLowerZ) + || (charCode >= _kCharUpperA && charCode <= _kCharUpperZ); +} + +const String _kPhysicalCapsLock = 'CapsLock'; + +const String _kLogicalDead = 'Dead'; + +const int _kWebKeyIdPlane = 0x00800000000; +const int _kAutogeneratedMask = 0x10000000000; + +// Bits in a Flutter logical event to generate the logical key for dead keys. +// +// Logical keys for dead keys are generated by annotating physical keys with +// modifiers (see `_getLogicalCode`). +const int _kDeadKeyCtrl = 0x100000000000; +const int _kDeadKeyShift = 0x200000000000; +const int _kDeadKeyAlt = 0x400000000000; +const int _kDeadKeyMeta = 0x800000000000; + +typedef DispatchKeyData = void Function(ui.KeyData data); + +/// Converts a floating number timestamp (in milliseconds) to a [Duration] by +/// splitting it into two integer components: milliseconds + microseconds. +Duration _eventTimeStampToDuration(num milliseconds) { + final int ms = milliseconds.toInt(); + final int micro = ((milliseconds - ms) * Duration.microsecondsPerMillisecond).toInt(); + return Duration(milliseconds: ms, microseconds: micro); +} + +class KeyboardBinding { + /// The singleton instance of this object. + static KeyboardBinding? get instance => _instance; + static KeyboardBinding? _instance; + + static void initInstance(html.Element glassPaneElement) { + if (_instance == null) { + _instance = KeyboardBinding._(glassPaneElement); + assert(() { + registerHotRestartListener(_instance!._reset); + return true; + }()); + } + } + + KeyboardBinding._(this.glassPaneElement) { + _setup(); + } + + final html.Element glassPaneElement; + late KeyboardConverter _converter; + final Map _listeners = {}; + + void _addEventListener(String eventName, html.EventListener handler) { + final html.EventListener loggedHandler = (html.Event event) { + if (_debugLogKeyEvents) { + print(event.type); + } + if (EngineSemanticsOwner.instance.receiveGlobalEvent(event)) { + handler(event); + } + }; + assert(!_listeners.containsKey(eventName)); + _listeners[eventName] = loggedHandler; + html.window.addEventListener(eventName, loggedHandler, true); + } + + /// Remove all active event listeners. + void _clearListeners() { + _listeners.forEach((String eventName, html.EventListener listener) { + html.window.removeEventListener(eventName, listener, true); + }); + _listeners.clear(); + } + void _onKeyData(ui.KeyData data) { + EnginePlatformDispatcher.instance.invokeOnKeyData(data); + } + + void _setup() { + _addEventListener('keydown', (html.Event event) { + return _converter.handleEvent(FlutterHtmlKeyboardEvent(event as html.KeyboardEvent)); + }); + _addEventListener('keyup', (html.Event event) { + return _converter.handleEvent(FlutterHtmlKeyboardEvent(event as html.KeyboardEvent)); + }); + _converter = KeyboardConverter(_onKeyData, onMacOs: operatingSystem == OperatingSystem.macOs); + } + + void _reset() { + _clearListeners(); + _converter.dispose(); + + _setup(); + } +} + +class AsyncKeyboardDispatching { + AsyncKeyboardDispatching({ + required this.keyData, + this.callback, + }); + + final ui.KeyData keyData; + final VoidCallback? callback; +} + +// A wrapper of [html.KeyboardEvent] with reduced methods delegated to the event +// for the convenience of testing. +class FlutterHtmlKeyboardEvent { + FlutterHtmlKeyboardEvent(this._event); + + final html.KeyboardEvent _event; + + String get type => _event.type; + String? get code => _event.code; + String? get key => _event.key; + bool? get repeat => _event.repeat; + int? get location => _event.location; + num? get timeStamp => _event.timeStamp; + bool get altKey => _event.altKey; + bool get ctrlKey => _event.ctrlKey; + bool get shiftKey => _event.shiftKey; + bool get metaKey => _event.metaKey; + + bool getModifierState(String key) => _event.getModifierState(key); + void preventDefault() => _event.preventDefault(); +} + +// Reads [html.KeyboardEvent], then [dispatches ui.KeyData] accordingly. +// +// The events are read through [handleEvent], and dispatched through the +// [dispatchKeyData] as given in the constructor. Some key data might be +// dispatched asynchronously. +class KeyboardConverter { + KeyboardConverter(this.dispatchKeyData, {this.onMacOs = false}); + + final DispatchKeyData dispatchKeyData; + final bool onMacOs; + + bool _disposed = false; + void dispose() { + _disposed = true; + } + + // On macOS, CapsLock behaves differently in that, a keydown event occurs when + // the key is pressed and the light turns on, while a keyup event occurs when the + // key is pressed and the light turns off. Flutter considers both events as + // key down, and synthesizes immediate cancel events following them. The state + // of "whether CapsLock is on" should be accessed by "activeLocks". + bool _shouldSynthesizeCapsLockUp() { + return onMacOs; + } + + Duration get _keydownCancelDuration => onMacOs ? _kKeydownCancelDurationMacOs : _kKeydownCancelDurationNormal; + + bool _shouldPreventDefault(FlutterHtmlKeyboardEvent event) { + switch (event.key) { + case 'Tab': + return true; + + default: + return false; + } + } + + static int _getPhysicalCode(String code) { + return kWebToPhysicalKey[code] ?? (code.hashCode + _kWebKeyIdPlane + _kAutogeneratedMask); + } + + static int _getModifierMask(FlutterHtmlKeyboardEvent event) { + final bool altDown = event.altKey; + final bool ctrlDown = event.ctrlKey; + final bool shiftDown = event.shiftKey; + final bool metaDown = event.metaKey; + return (altDown ? _kDeadKeyAlt : 0) + + (ctrlDown ? _kDeadKeyCtrl : 0) + + (shiftDown ? _kDeadKeyShift : 0) + + (metaDown ? _kDeadKeyMeta : 0); + } + + // Whether `event.key` should be considered a key name. + // + // The `event.key` can either be a key name or the printable character. If the + // first character is an alphabet, it must be either 'A' to 'Z' ( and return + // true), or be a key name (and return false). Otherwise, return true. + static bool _eventKeyIsKeyname(String key) { + assert(key.length > 0); + return isAlphabet(key.codeUnitAt(0)) && key.length > 1; + } + + static int _characterToLogicalKey(String key) { + // Assume the length being <= 2 to be sufficient in all cases. If not, + // extend the algorithm. + assert(key.length <= 2); + int result = key.codeUnitAt(0) & 0xffff; + if (key.length == 2) { + result += key.codeUnitAt(1) << 16; + } + // Convert upper letters to lower letters + if (result >= _kCharUpperA && result <= _kCharUpperZ) { + result = result + _kCharLowerA - _kCharUpperA; + } + return result; + } + + static int _deadKeyToLogicalKey(int physicalKey, FlutterHtmlKeyboardEvent event) { + // 'Dead' is used to represent dead keys, such as a diacritic to the + // following base letter (such as Option-e results in ´). + // + // Assume they can be told apart with the physical key and the modifiers + // pressed. + return physicalKey + _getModifierMask(event) + _kWebKeyIdPlane + _kAutogeneratedMask; + } + + static int _otherLogicalKey(String key) { + return kWebToLogicalKey[key] ?? (key.hashCode + _kWebKeyIdPlane + _kAutogeneratedMask); + } + + // Map from pressed physical key to corresponding pressed logical key. + // + // Multiple physical keys can be mapped to the same logical key, usually due + // to positioned keys (left/right/numpad) or multiple keyboards. + final Map _pressingRecords = {}; + + // Schedule the dispatching of an event in the future. The `callback` will + // invoked before that. + // + // Returns a callback that cancels the schedule. Disposal of + // `KeyBoardConverter` also cancels the shedule automatically. + VoidCallback _scheduleAsyncEvent(Duration duration, ValueGetter getData, VoidCallback callback) { + bool canceled = false; + Future.delayed(duration).then((_) { + if (!canceled && !_disposed) { + callback(); + dispatchKeyData(getData()); + } + }); + return () { canceled = true; }; + } + + // ## About Key guards + // + // When the user enters a browser/system shortcut (e.g. `cmd+alt+i`) the + // browser doesn't send a keyup for it. This puts the framework in a corrupt + // state because it thinks the key was never released. + // + // To avoid this, we rely on the fact that browsers send repeat events + // while the key is held down by the user. If we don't receive a repeat + // event within a specific duration ([_keydownCancelDuration]) we assume + // the user has released the key and we synthesize a keyup event. + final Map _keyGuards = {}; + // Call this method on the down or repeated event of a non-modifier key. + void _startGuardingKey(int physicalKey, int logicalKey, Duration currentTimeStamp) { + final VoidCallback cancelingCallback = _scheduleAsyncEvent( + _keydownCancelDuration, + () => ui.KeyData( + timeStamp: currentTimeStamp + _keydownCancelDuration, + change: ui.KeyChange.up, + physical: physicalKey, + logical: logicalKey, + character: null, + synthesized: true, + ), + () { + _pressingRecords.remove(physicalKey); + } + ); + _keyGuards.remove(physicalKey)?.call(); + _keyGuards[physicalKey] = cancelingCallback; + } + // Call this method on an up event event of a non-modifier key. + void _stopGuardingKey(int physicalKey) { + _keyGuards.remove(physicalKey)?.call(); + } + + // Parse the HTML event, update states, and dispatch Flutter key data through + // [dispatchKeyData]. + // + // * The method might dispatch some synthesized key data first to update states, + // results discarded. + // * Then it dispatches exactly one non-synthesized key data that corresponds + // to the `event`, i.e. the main key data. The result of this dispatching is + // returned to indicate whether the event is processed by Flutter. + // * Some key data might be synthesized to update states after the main key + // data. They are always scheduled asynchronously with results discarded. + bool handleEvent(FlutterHtmlKeyboardEvent event) { + final Duration timeStamp = _eventTimeStampToDuration(event.timeStamp!); + + if (_shouldPreventDefault(event)) { + event.preventDefault(); + } + final String eventKey = event.key!; + + final int physicalKey = _getPhysicalCode(event.code!); + final bool logicalKeyIsCharacter = !_eventKeyIsKeyname(eventKey); + final String? character = logicalKeyIsCharacter ? eventKey : null; + final int logicalKey = () { + if (kWebLogicalLocationMap.containsKey(event.key!)) { + final int? result = kWebLogicalLocationMap[event.key!]?[event.location!]; + assert(result != null, 'Invalid modifier location: ${event.key}, ${event.location}'); + return result!; + } + if (character != null) + return _characterToLogicalKey(character); + if (eventKey == _kLogicalDead) + return _deadKeyToLogicalKey(physicalKey, event); + return _otherLogicalKey(eventKey); + }(); + + assert(event.type == 'keydown' || event.type == 'keyup'); + final bool isPhysicalDown = event.type == 'keydown' || + // On macOS, both keydown and keyup events of CapsLock should be considered keydown, + // followed by an immediate cancel event. + (_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock); + + final int? lastLogicalRecord = _pressingRecords[physicalKey]; + + ui.KeyChange change; + + if (_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock) { + // Case 1: Handle CapsLock on macOS + // + // On macOS, both keydown and keyup events of CapsLock are considered + // keydown, followed by an immediate synchronized up event. + + _scheduleAsyncEvent( + Duration.zero, + () => ui.KeyData( + timeStamp: timeStamp, + change: ui.KeyChange.up, + physical: physicalKey, + logical: logicalKey, + character: null, + synthesized: true, + ), + () { + _pressingRecords.remove(physicalKey); + } + ); + change = ui.KeyChange.down; + + } else if (isPhysicalDown) { + // Case 2: Handle key down of normal keys + change = ui.KeyChange.down; + if (lastLogicalRecord != null) { + // This physical key is being pressed according to the record. + if (event.repeat ?? false) { + // A normal repeated key. + change = ui.KeyChange.repeat; + } else { + // A non-repeated key has been pressed that has the exact physical key as + // a currently pressed one, usually indicating multiple keyboards are + // pressing keys with the same physical key, or the up event was lost + // during a loss of focus. The down event is ignored. + return false; + } + } else { + // This physical key is not being pressed according to the record. It's a + // normal down event, whether the system event is a repeat or not. + } + + } else { // isPhysicalDown is false and not CapsLock + // Case 2: Handle key up of normal keys + if (lastLogicalRecord == null) { + // The physical key has been released before. It indicates multiple + // keyboards pressed keys with the same physical key. Ignore the up event. + return false; + } + + change = ui.KeyChange.up; + } + + late final int? nextLogicalRecord; + switch (change) { + case ui.KeyChange.down: + assert(lastLogicalRecord == null); + nextLogicalRecord = logicalKey; + break; + case ui.KeyChange.up: + assert(lastLogicalRecord != null); + nextLogicalRecord = null; + break; + case ui.KeyChange.repeat: + assert(lastLogicalRecord != null); + nextLogicalRecord = lastLogicalRecord; + break; + } + if (nextLogicalRecord == null) { + _pressingRecords.remove(physicalKey); + } else { + _pressingRecords[physicalKey] = nextLogicalRecord; + } + + // After updating _pressingRecords, synchronize modifier states. The + // `event.***Key` fields can be used to reduce some omitted modifier key + // events. We can deduce key cancel events if they are false. Key sync + // events can not be deduced since we don't know which physical key they + // represent. + _kLogicalKeyToModifierGetter.forEach((int logicalKey, _ModifierGetter getModifier) { + // print(_pressingRecords); + if (_pressingRecords.containsValue(logicalKey) && !getModifier(event)) { + _pressingRecords.removeWhere((int physicalKey, int logicalRecord) { + if (logicalRecord != logicalKey) + return false; + + dispatchKeyData(ui.KeyData( + timeStamp: timeStamp, + change: ui.KeyChange.up, + physical: physicalKey, + logical: logicalKey, + character: null, + synthesized: true, + )); + + return true; + }); + } + }); + + // Update key guards + if (logicalKeyIsCharacter) { + if (nextLogicalRecord != null) { + _startGuardingKey(physicalKey, logicalKey, timeStamp); + } else { + _stopGuardingKey(physicalKey); + } + } + + final ui.KeyData keyData = ui.KeyData( + timeStamp: timeStamp, + change: change, + physical: physicalKey, + logical: lastLogicalRecord ?? logicalKey, + character: change == ui.KeyChange.up ? null : character, + synthesized: false, + ); + + dispatchKeyData(keyData); + return true; + } +} diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index 7f6ce039c5a5c..83ebe95725adf 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -170,6 +170,31 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { invoke1(_onPointerDataPacket, _onPointerDataPacketZone, dataPacket); } + /// A callback that is invoked when pointer data is available. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + /// + /// See also: + /// + /// * [GestureBinding], the Flutter framework class which manages pointer + /// events. + @override + ui.KeyDataCallback? get onKeyData => _onKeyData; + ui.KeyDataCallback? _onKeyData; + Zone? _onKeyDataZone; + @override + set onKeyData(ui.KeyDataCallback? callback) { + _onKeyData = callback; + _onKeyDataZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnKeyData(ui.KeyData dataPacket) { + invoke1(_onKeyData, _onKeyDataZone, dataPacket); + } + /// A callback that is invoked to report the [FrameTiming] of recently /// rasterized frames. /// @@ -947,4 +972,3 @@ void invoke3(void Function(A1 a1, A2 a2, A3 a3)? callback, Zone? zon }); } } - diff --git a/lib/web_ui/lib/src/ui/key.dart b/lib/web_ui/lib/src/ui/key.dart new file mode 100644 index 0000000000000..ffb8d4e81064b --- /dev/null +++ b/lib/web_ui/lib/src/ui/key.dart @@ -0,0 +1,66 @@ +// 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. + +// @dart = 2.12 + +part of ui; + +/// How the key has changed since the last report. +enum KeyChange { + /// The key is pressed. + down, + + /// The key is released. + up, + + /// The key is held, causing a repeated key input. + repeat, +} + +/// Information about the change of a key. +class KeyData { + /// Creates an object that represents the change of a key. + const KeyData({ + required this.timeStamp, + required this.change, + required this.physical, + required this.logical, + required this.character, + required this.synthesized, + }); + + /// Time of event dispatch, relative to an arbitrary timeline. + /// + /// For [KeyChange.synchronize] and [KeyChange.cancel] events, the [timeStamp] + /// might not be the actual time that the key press or release happens. + final Duration timeStamp; + + /// How the key has changed since the last report. + final KeyChange change; + + /// The key code for the physical key that has changed. + final int physical; + + /// The key code for the logical key that has changed. + final int logical; + + /// Character input from the event. + /// + /// Not available to up events. + final String? character; + + /// Whether the event is generated by Flutter, hence not corresponding to a + /// native event. + final bool synthesized; + + @override + String toString() => 'KeyData(timeStamp: $timeStamp, change: $change, physical: $physical, ' + 'logical: $logical, character: $character, synthesized: $synthesized)'; + + /// Returns a complete textual description of the information in this object. + String toStringFull() { + return '$runtimeType(' + ')'; + } +} diff --git a/lib/web_ui/lib/src/ui/platform_dispatcher.dart b/lib/web_ui/lib/src/ui/platform_dispatcher.dart index 122e2befd336c..96aec518b4f0b 100644 --- a/lib/web_ui/lib/src/ui/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/ui/platform_dispatcher.dart @@ -9,6 +9,7 @@ typedef VoidCallback = void Function(); typedef FrameCallback = void Function(Duration duration); typedef TimingsCallback = void Function(List timings); typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); +typedef KeyDataCallback = void Function(KeyData packet); typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); typedef PlatformMessageResponseCallback = void Function(ByteData? data); typedef PlatformMessageCallback = void Function( @@ -36,6 +37,9 @@ abstract class PlatformDispatcher { PointerDataPacketCallback? get onPointerDataPacket; set onPointerDataPacket(PointerDataPacketCallback? callback); + KeyDataCallback? get onKeyData; + set onKeyData(KeyDataCallback? callback); + TimingsCallback? get onReportTimings; set onReportTimings(TimingsCallback? callback); @@ -417,4 +421,4 @@ class Locale { } return out.toString(); } -} \ No newline at end of file +} diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index 6fc8b901cd742..ef3452947b5a1 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -81,6 +81,11 @@ abstract class SingletonFlutterWindow extends FlutterWindow { platformDispatcher.onPointerDataPacket = callback; } + KeyDataCallback? get onKeyData => platformDispatcher.onKeyData; + set onKeyData(KeyDataCallback? callback) { + platformDispatcher.onKeyData = callback; + } + String get defaultRouteName => platformDispatcher.defaultRouteName; void scheduleFrame() => platformDispatcher.scheduleFrame(); diff --git a/lib/web_ui/lib/ui.dart b/lib/web_ui/lib/ui.dart index c64360fec5403..334887d873c3d 100644 --- a/lib/web_ui/lib/ui.dart +++ b/lib/web_ui/lib/ui.dart @@ -24,6 +24,7 @@ part 'src/ui/compositing.dart'; part 'src/ui/geometry.dart'; part 'src/ui/hash_codes.dart'; part 'src/ui/initialization.dart'; +part 'src/ui/key.dart'; part 'src/ui/lerp.dart'; part 'src/ui/natives.dart'; part 'src/ui/painting.dart'; diff --git a/lib/web_ui/test/keyboard_converter_test.dart b/lib/web_ui/test/keyboard_converter_test.dart new file mode 100644 index 0000000000000..eb5bcf5563dfd --- /dev/null +++ b/lib/web_ui/test/keyboard_converter_test.dart @@ -0,0 +1,927 @@ +// 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. + +// @dart = 2.10 + +import 'package:quiver/testing/async.dart'; +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; +import 'package:meta/meta.dart' show isTest; + +const int kLocationLeft = 1; +const int kLocationRight = 2; +const int kLocationNumpad = 3; + +const int kPhysicalKeyA = 0x00070004; +const int kPhysicalKeyE = 0x00070008; +const int kPhysicalKeyU = 0x00070018; +const int kPhysicalDigit1 = 0x0007001e; +const int kPhysicalNumpad1 = 0x00070059; +const int kPhysicalShiftLeft = 0x000700e1; +const int kPhysicalShiftRight = 0x000700e5; +const int kPhysicalMetaLeft = 0x000700e3; +const int kPhysicalTab = 0x0007002b; +const int kPhysicalCapsLock = 0x00070039; +const int kPhysicalScrollLock = 0x00070047; + +const int kLogicalKeyA = 0x00000000041; +const int kLogicalKeyU = 0x00000000055; +const int kLogicalDigit1 = 0x00000000031; +const int kLogicalNumpad1 = 0x00200000031; +const int kLogicalShiftLeft = 0x030000010d; +const int kLogicalShiftRight = 0x040000010d; +const int kLogicalMetaLeft = 0x0300000109; +const int kLogicalTab = 0x0000000009; +const int kLogicalCapsLock = 0x00000000104; +const int kLogicalScrollLock = 0x0000000010c; + +typedef VoidCallback = void Function(); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + test('Single key press, repeat, and release', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + bool preventedDefault = false; + final onPreventDefault = () { preventedDefault = true; }; + + converter.handleEvent(keyDownEvent('KeyA', 'a') + ..timeStamp = 1 + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + timeStamp: Duration(milliseconds: 1), + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + expect(preventedDefault, false); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a') + ..timeStamp = 1.5 + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + timeStamp: Duration(milliseconds: 1, microseconds: 500), + change: ui.KeyChange.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + expect(preventedDefault, false); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a') + ..timeStamp = 1500 + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + timeStamp: Duration(seconds: 1, milliseconds: 500), + change: ui.KeyChange.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + expect(preventedDefault, false); + + converter.handleEvent(keyUpEvent('KeyA', 'a') + ..timeStamp = 2000.5 + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + timeStamp: Duration(seconds: 2, microseconds: 500), + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + expect(preventedDefault, false); + }); + + test('Release modifier during a repeated sequence', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + + converter.handleEvent(keyDownEvent('KeyA', 'A', kShift)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'A', + ); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'A', kShift)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'A', + ); + + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + + converter.handleEvent(keyUpEvent('KeyA', 'a')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + }); + + test('Distinguish between left and right modifiers', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + + converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalShiftRight, + logical: kLogicalShiftRight, + character: null, + ); + + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + + converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalShiftRight, + logical: kLogicalShiftRight, + character: null, + ); + }); + + test('Distinguish between normal and numpad digits', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + converter.handleEvent(keyDownEvent('Digit1', '1', 0, 0)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalDigit1, + logical: kLogicalDigit1, + character: '1', + ); + + converter.handleEvent(keyDownEvent('Numpad1', '1', 0, kLocationNumpad)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalNumpad1, + logical: kLogicalNumpad1, + character: '1', + ); + + converter.handleEvent(keyUpEvent('Digit1', '1', 0, 0)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalDigit1, + logical: kLogicalDigit1, + character: null, + ); + + converter.handleEvent(keyUpEvent('Numpad1', '1', 0, kLocationNumpad)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalNumpad1, + logical: kLogicalNumpad1, + character: null, + ); + }); + + test('Prevents default when pressing Tab', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + bool preventedDefault = false; + final onPreventDefault = () { preventedDefault = true; }; + + converter.handleEvent(keyDownEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalTab, + logical: kLogicalTab, + character: null, + ); + expect(preventedDefault, true); + preventedDefault = false; + + converter.handleEvent(keyUpEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalTab, + logical: kLogicalTab, + character: null, + ); + expect(preventedDefault, true); + preventedDefault = false; + }); + + test('Dead keys are distinguishable', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + // The absolute values of the following logical keys are not guaranteed. + const int kLogicalAltE = 0x410800070008; + const int kLogicalAltU = 0x410800070018; + const int kLogicalAltShiftE = 0x610800070008; + // The values must be distinguishable. + expect(kLogicalAltE, isNot(equals(kLogicalAltU))); + expect(kLogicalAltE, isNot(equals(kLogicalAltShiftE))); + + converter.handleEvent(keyDownEvent('AltLeft', 'Alt', kAlt, kLocationLeft)); + + converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalKeyE, + logical: kLogicalAltE, + character: null, + ); + + converter.handleEvent(keyUpEvent('KeyE', 'Dead', kAlt)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalKeyE, + logical: kLogicalAltE, + character: null, + ); + + converter.handleEvent(keyDownEvent('KeyU', 'Dead', kAlt)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalKeyU, + logical: kLogicalAltU, + character: null, + ); + + converter.handleEvent(keyUpEvent('KeyU', 'Dead', kAlt)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalKeyU, + logical: kLogicalAltU, + character: null, + ); + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kAlt | kShift, kLocationLeft)); + + // This does not actually produce a Dead key on macOS (US layout); just for + // testing. + converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt | kShift)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalKeyE, + logical: kLogicalAltShiftE, + character: null, + ); + + converter.handleEvent(keyUpEvent('AltLeft', 'Alt', kShift, kLocationLeft)); + + converter.handleEvent(keyUpEvent('KeyE', 'e', kShift)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalKeyE, + logical: kLogicalAltShiftE, + character: null, + ); + + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); + }); + + test('Duplicate down is ignored', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + // A KeyUp of ShiftLeft is missed due to loss of focus. + + keyDataList.clear(); + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + expect(keyDataList, isEmpty); + + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + }); + + test('Duplicate ups are skipped', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + // A KeyDown of ShiftRight is missed due to loss of focus. + converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight)); + expect(keyDataList, isEmpty); + }); + + test('Conflict from multiple keyboards do not crash', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + // Same layout + converter.handleEvent(keyDownEvent('KeyA', 'a')); + converter.handleEvent(keyDownEvent('KeyA', 'a')); + converter.handleEvent(keyUpEvent('KeyA', 'a')); + converter.handleEvent(keyUpEvent('KeyA', 'a')); + + // Different layout + converter.handleEvent(keyDownEvent('KeyA', 'a')); + converter.handleEvent(keyDownEvent('KeyA', 'u')); + converter.handleEvent(keyUpEvent('KeyA', 'u')); + converter.handleEvent(keyUpEvent('KeyA', 'a')); + + // Passes if there's no crash, and states are reset after everything is released. + keyDataList.clear(); + converter.handleEvent(keyDownEvent('KeyA', 'a')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + + converter.handleEvent(keyDownEvent('KeyU', 'u')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalKeyU, + logical: kLogicalKeyU, + character: 'u', + ); + }); + + testFakeAsync('CapsLock down synthesizes an immediate cancel on macOS', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }, onMacOs: true); + + converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + keyDataList.clear(); + + async.elapse(Duration(microseconds: 1)); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + synthesized: true, + ); + keyDataList.clear(); + + converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + keyDataList.clear(); + + async.elapse(Duration(microseconds: 1)); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + synthesized: true, + ); + keyDataList.clear(); + + // Another key down works + converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + keyDataList.clear(); + + + // Schedules are canceled after disposal + converter.dispose(); + async.elapse(Duration(seconds: 10)); + expect(keyDataList, isEmpty); + }); + + testFakeAsync('CapsLock behaves normally on non-macOS', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }, onMacOs: false); + + converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + keyDataList.clear(); + + async.elapse(Duration(seconds: 10)); + expect(keyDataList, isEmpty); + + converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + keyDataList.clear(); + + async.elapse(Duration(seconds: 10)); + expect(keyDataList, isEmpty); + + converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + + converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + }); + + testFakeAsync('Key guards: key down events are guarded', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); + async.elapse(Duration(milliseconds: 100)); + + converter.handleEvent(keyDownEvent('KeyA', 'a', kMeta)..timeStamp = 200); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 200), + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + keyDataList.clear(); + + // Keyup of KeyA is omitted due to being a shortcut. + + async.elapse(Duration(milliseconds: 2500)); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 1200), + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + synthesized: true, + ); + keyDataList.clear(); + + converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 2700); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2700), + change: ui.KeyChange.up, + physical: kPhysicalMetaLeft, + logical: kLogicalMetaLeft, + character: null, + ); + async.elapse(Duration(milliseconds: 100)); + + // Key A states are cleared + converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2800), + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + async.elapse(Duration(milliseconds: 100)); + + converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2900), + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + }); + + testFakeAsync('Key guards: key repeated down events refreshes guards', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); + async.elapse(Duration(milliseconds: 100)); + + converter.handleEvent(keyDownEvent('KeyA', 'a', kMeta)..timeStamp = 200); + async.elapse(Duration(milliseconds: 400)); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a', kMeta)..timeStamp = 600); + async.elapse(Duration(milliseconds: 50)); + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a', kMeta)..timeStamp = 650); + async.elapse(Duration(milliseconds: 50)); + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a', kMeta)..timeStamp = 700); + + // Keyup of KeyA is omitted due to being a shortcut. + + async.elapse(Duration(milliseconds: 2500)); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 1700), + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + synthesized: true, + ); + keyDataList.clear(); + + converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 3200); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 3200), + change: ui.KeyChange.up, + physical: kPhysicalMetaLeft, + logical: kLogicalMetaLeft, + character: null, + ); + async.elapse(Duration(milliseconds: 100)); + + // Key A states are cleared + converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 3300); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 3300), + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + async.elapse(Duration(milliseconds: 100)); + + converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 3400); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 3400), + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + }); + + testFakeAsync('Key guards: cleared by keyups', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }); + + converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); + async.elapse(Duration(milliseconds: 100)); + + converter.handleEvent(keyDownEvent('KeyA', 'a', kCtrl)..timeStamp = 200); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 200), + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + keyDataList.clear(); + async.elapse(Duration(milliseconds: 500)); + + converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 700); + async.elapse(Duration(milliseconds: 100)); + + converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 800); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 800), + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + keyDataList.clear(); + async.elapse(Duration(milliseconds: 2000)); + expect(keyDataList, isEmpty); + + // Key A states are cleared + converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2800), + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + async.elapse(Duration(milliseconds: 100)); + + converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2900), + change: ui.KeyChange.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + }); + + testFakeAsync('Lock flags of other keys', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }, onMacOs: false); + + converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalScrollLock, + logical: kLogicalScrollLock, + character: null, + ); + keyDataList.clear(); + + async.elapse(Duration(seconds: 10)); + expect(keyDataList, isEmpty); + + converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalScrollLock, + logical: kLogicalScrollLock, + character: null, + ); + keyDataList.clear(); + + converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalScrollLock, + logical: kLogicalScrollLock, + character: null, + ); + + converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); + expectKeyData(keyDataList.last, + change: ui.KeyChange.up, + physical: kPhysicalScrollLock, + logical: kLogicalScrollLock, + character: null, + ); + }); + + test('Deduce modifier key cancel from modifier field', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + }, onMacOs: false); + + converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalShiftRight, + logical: kLogicalShiftRight, + character: null, + ); + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + keyDataList.clear(); + + // The release of the shift keys are omitted + + converter.handleEvent(keyDownEvent('KeyA', 'a')); + expect(keyDataList, hasLength(3)); + expectKeyData(keyDataList[0], + change: ui.KeyChange.up, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + synthesized: true, + ); + expectKeyData(keyDataList[1], + change: ui.KeyChange.up, + physical: kPhysicalShiftRight, + logical: kLogicalShiftRight, + character: null, + synthesized: true, + ); + expectKeyData(keyDataList.last, + change: ui.KeyChange.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + }); +} + +class MockKeyboardEvent implements FlutterHtmlKeyboardEvent { + MockKeyboardEvent({ + required this.type, + required this.code, + required this.key, + this.timeStamp = 0, + this.repeat = false, + this.altKey = false, + this.ctrlKey = false, + this.shiftKey = false, + this.metaKey = false, + this.location = 0, + this.onPreventDefault, + }); + + String type; + String? code; + String? key; + bool? repeat; + num? timeStamp; + bool altKey; + bool ctrlKey; + bool shiftKey; + bool metaKey; + int? location; + + bool getModifierState(String key) => modifierState.contains(key); + final Set modifierState = {}; + + void preventDefault() { onPreventDefault?.call(); } + VoidCallback? onPreventDefault; +} + +// Flags used for the `modifiers` argument of `key***Event` functions. +const kAlt = 0x1; +const kCtrl = 0x2; +const kShift = 0x4; +const kMeta = 0x8; + +// Utility functions to make code more concise. +// +// To add timeStamp or onPreventDefault, use syntax like `..timeStamp = `. +MockKeyboardEvent keyDownEvent(String code, String key, [int modifiers = 0, int location = 0]) { + return MockKeyboardEvent( + type: 'keydown', + code: code, + key: key, + altKey: modifiers & kAlt != 0, + ctrlKey: modifiers & kCtrl != 0, + shiftKey: modifiers & kShift != 0, + metaKey: modifiers & kMeta != 0, + location: location, + ); +} + +MockKeyboardEvent keyUpEvent(String code, String key, [int modifiers = 0, int location = 0]) { + return MockKeyboardEvent( + type: 'keyup', + code: code, + key: key, + altKey: modifiers & kAlt != 0, + ctrlKey: modifiers & kCtrl != 0, + shiftKey: modifiers & kShift != 0, + metaKey: modifiers & kMeta != 0, + location: location, + ); +} + +MockKeyboardEvent keyRepeatedDownEvent(String code, String key, [int modifiers = 0, int location = 0]) { + return MockKeyboardEvent( + type: 'keydown', + code: code, + key: key, + altKey: modifiers & kAlt != 0, + ctrlKey: modifiers & kCtrl != 0, + shiftKey: modifiers & kShift != 0, + metaKey: modifiers & kMeta != 0, + repeat: true, + location: location, + ); +} + +// Flags used for the `activeLocks` argument of expectKeyData. +const kCapsLock = 0x1; +const kNumlLock = 0x2; +const kScrollLock = 0x4; + +void expectKeyData( + ui.KeyData target, { + required ui.KeyChange change, + required int physical, + required int logical, + required String? character, + Duration? timeStamp, + bool synthesized = false, +}) { + expect(target.change, change); + expect(target.physical, physical); + expect(target.logical, logical); + expect(target.character, character); + expect(target.synthesized, synthesized); + if (timeStamp != null) + expect(target.timeStamp, equals(timeStamp)); +} + +typedef FakeAsyncTest = void Function(FakeAsync); + +@isTest +void testFakeAsync(String description, FakeAsyncTest fn) { + test(description, () { + FakeAsync().run(fn); + }); +} From a11b10a72c437087d5631ba66e326ad6048edb31 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 15:12:47 -0800 Subject: [PATCH 03/82] Fix license --- shell/platform/windows/flutter_key_map.cc | 2 +- shell/platform/windows/flutter_keyboard_manager.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/windows/flutter_key_map.cc b/shell/platform/windows/flutter_key_map.cc index a0b662baf74e8..8ddb4f80fad84 100644 --- a/shell/platform/windows/flutter_key_map.cc +++ b/shell/platform/windows/flutter_key_map.cc @@ -1,4 +1,4 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. +// 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. diff --git a/shell/platform/windows/flutter_keyboard_manager.h b/shell/platform/windows/flutter_keyboard_manager.h index 42c11d04f6e9b..1d738d9b59a8f 100644 --- a/shell/platform/windows/flutter_keyboard_manager.h +++ b/shell/platform/windows/flutter_keyboard_manager.h @@ -1,4 +1,4 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. +// 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. From 1e7d99c1c9a994ad706ba42657fd1c505ba8695b Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 15:14:13 -0800 Subject: [PATCH 04/82] Correctly remove printing --- shell/platform/windows/flutter_keyboard_manager.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/shell/platform/windows/flutter_keyboard_manager.cc b/shell/platform/windows/flutter_keyboard_manager.cc index 844cec76bf5d7..dd86e3cface70 100644 --- a/shell/platform/windows/flutter_keyboard_manager.cc +++ b/shell/platform/windows/flutter_keyboard_manager.cc @@ -129,9 +129,6 @@ void FlutterKeyboardManager::KeyboardHook(FlutterWindowsView* view, int action, char32_t character, bool was_down) { - action == WM_KEYDOWN ? 'D' : 'U', - key, scancode, character, was_down - );fflush(stdout); const uint64_t physical_key = getPhysicalKey(scancode); const uint64_t logical_key = getLogicalKey(key, scancode); assert(action == WM_KEYDOWN || action == WM_KEYUP); From 4d43b27f8fb1a6c8ab5257164cb558b214a22b34 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 16:06:22 -0800 Subject: [PATCH 05/82] Fix license and key map format --- ci/licenses_golden/licenses_flutter | 9 + shell/platform/windows/flutter_key_map.cc | 588 +++++++++++----------- 2 files changed, 303 insertions(+), 294 deletions(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 12543e387ecfc..4f036c4b35988 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1091,6 +1091,7 @@ FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.cc FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.h FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.cc FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.h +FILE: ../../../flutter/lib/ui/key.dart FILE: ../../../flutter/lib/ui/lerp.dart FILE: ../../../flutter/lib/ui/natives.dart FILE: ../../../flutter/lib/ui/painting.dart @@ -1178,6 +1179,10 @@ FILE: ../../../flutter/lib/ui/ui_dart_state.h FILE: ../../../flutter/lib/ui/volatile_path_tracker.cc FILE: ../../../flutter/lib/ui/volatile_path_tracker.h FILE: ../../../flutter/lib/ui/window.dart +FILE: ../../../flutter/lib/ui/window/key_data.cc +FILE: ../../../flutter/lib/ui/window/key_data.h +FILE: ../../../flutter/lib/ui/window/key_data_packet.cc +FILE: ../../../flutter/lib/ui/window/key_data_packet.h FILE: ../../../flutter/lib/ui/window/platform_configuration.cc FILE: ../../../flutter/lib/ui/window/platform_configuration.h FILE: ../../../flutter/lib/ui/window/platform_configuration_unittests.cc @@ -2185,6 +2190,10 @@ FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/plu FILE: ../../../flutter/shell/platform/windows/client_wrapper/plugin_registrar_windows_unittests.cc FILE: ../../../flutter/shell/platform/windows/cursor_handler.cc FILE: ../../../flutter/shell/platform/windows/cursor_handler.h +FILE: ../../../flutter/shell/platform/windows/flutter_key_map.cc +FILE: ../../../flutter/shell/platform/windows/flutter_keyboard_manager.cc +FILE: ../../../flutter/shell/platform/windows/flutter_keyboard_manager.h +FILE: ../../../flutter/shell/platform/windows/flutter_keyboard_manager_unittests.cc FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle.cc FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle.h FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle_unittests.cc diff --git a/shell/platform/windows/flutter_key_map.cc b/shell/platform/windows/flutter_key_map.cc index 8ddb4f80fad84..bac421a3918d0 100644 --- a/shell/platform/windows/flutter_key_map.cc +++ b/shell/platform/windows/flutter_key_map.cc @@ -1,4 +1,4 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. +// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -19,305 +19,305 @@ namespace flutter { std::map FlutterKeyboardManager::windowsToPhysicalMap_ = { - { 0x0000e05f, 0x00010082 }, // sleep - { 0x0000e063, 0x00010083 }, // wakeUp - { 0x000000ff, 0x00070001 }, // usbErrorRollOver - { 0x000000fc, 0x00070002 }, // usbPostFail - { 0x0000001e, 0x00070004 }, // keyA - { 0x00000030, 0x00070005 }, // keyB - { 0x0000002e, 0x00070006 }, // keyC - { 0x00000020, 0x00070007 }, // keyD - { 0x00000012, 0x00070008 }, // keyE - { 0x00000021, 0x00070009 }, // keyF - { 0x00000022, 0x0007000a }, // keyG - { 0x00000023, 0x0007000b }, // keyH - { 0x00000017, 0x0007000c }, // keyI - { 0x00000024, 0x0007000d }, // keyJ - { 0x00000025, 0x0007000e }, // keyK - { 0x00000026, 0x0007000f }, // keyL - { 0x00000032, 0x00070010 }, // keyM - { 0x00000031, 0x00070011 }, // keyN - { 0x00000018, 0x00070012 }, // keyO - { 0x00000019, 0x00070013 }, // keyP - { 0x00000010, 0x00070014 }, // keyQ - { 0x00000013, 0x00070015 }, // keyR - { 0x0000001f, 0x00070016 }, // keyS - { 0x00000014, 0x00070017 }, // keyT - { 0x00000016, 0x00070018 }, // keyU - { 0x0000002f, 0x00070019 }, // keyV - { 0x00000011, 0x0007001a }, // keyW - { 0x0000002d, 0x0007001b }, // keyX - { 0x00000015, 0x0007001c }, // keyY - { 0x0000002c, 0x0007001d }, // keyZ - { 0x00000002, 0x0007001e }, // digit1 - { 0x00000003, 0x0007001f }, // digit2 - { 0x00000004, 0x00070020 }, // digit3 - { 0x00000005, 0x00070021 }, // digit4 - { 0x00000006, 0x00070022 }, // digit5 - { 0x00000007, 0x00070023 }, // digit6 - { 0x00000008, 0x00070024 }, // digit7 - { 0x00000009, 0x00070025 }, // digit8 - { 0x0000000a, 0x00070026 }, // digit9 - { 0x0000000b, 0x00070027 }, // digit0 - { 0x0000001c, 0x00070028 }, // enter - { 0x00000001, 0x00070029 }, // escape - { 0x0000000e, 0x0007002a }, // backspace - { 0x0000000f, 0x0007002b }, // tab - { 0x00000039, 0x0007002c }, // space - { 0x0000000c, 0x0007002d }, // minus - { 0x0000000d, 0x0007002e }, // equal - { 0x0000001a, 0x0007002f }, // bracketLeft - { 0x0000001b, 0x00070030 }, // bracketRight - { 0x0000002b, 0x00070031 }, // backslash - { 0x00000027, 0x00070033 }, // semicolon - { 0x00000028, 0x00070034 }, // quote - { 0x00000029, 0x00070035 }, // backquote - { 0x00000033, 0x00070036 }, // comma - { 0x00000034, 0x00070037 }, // period - { 0x00000035, 0x00070038 }, // slash - { 0x0000003a, 0x00070039 }, // capsLock - { 0x0000003b, 0x0007003a }, // f1 - { 0x0000003c, 0x0007003b }, // f2 - { 0x0000003d, 0x0007003c }, // f3 - { 0x0000003e, 0x0007003d }, // f4 - { 0x0000003f, 0x0007003e }, // f5 - { 0x00000040, 0x0007003f }, // f6 - { 0x00000041, 0x00070040 }, // f7 - { 0x00000042, 0x00070041 }, // f8 - { 0x00000043, 0x00070042 }, // f9 - { 0x00000044, 0x00070043 }, // f10 - { 0x00000057, 0x00070044 }, // f11 - { 0x00000058, 0x00070045 }, // f12 - { 0x0000e037, 0x00070046 }, // printScreen - { 0x00000046, 0x00070047 }, // scrollLock - { 0x00000045, 0x00070048 }, // pause - { 0x0000e052, 0x00070049 }, // insert - { 0x0000e047, 0x0007004a }, // home - { 0x0000e049, 0x0007004b }, // pageUp - { 0x0000e053, 0x0007004c }, // delete - { 0x0000e04f, 0x0007004d }, // end - { 0x0000e051, 0x0007004e }, // pageDown - { 0x0000e04d, 0x0007004f }, // arrowRight - { 0x0000e04b, 0x00070050 }, // arrowLeft - { 0x0000e050, 0x00070051 }, // arrowDown - { 0x0000e048, 0x00070052 }, // arrowUp - { 0x0000e045, 0x00070053 }, // numLock - { 0x0000e035, 0x00070054 }, // numpadDivide - { 0x00000037, 0x00070055 }, // numpadMultiply - { 0x0000004a, 0x00070056 }, // numpadSubtract - { 0x0000004e, 0x00070057 }, // numpadAdd - { 0x0000e01c, 0x00070058 }, // numpadEnter - { 0x0000004f, 0x00070059 }, // numpad1 - { 0x00000050, 0x0007005a }, // numpad2 - { 0x00000051, 0x0007005b }, // numpad3 - { 0x0000004b, 0x0007005c }, // numpad4 - { 0x0000004c, 0x0007005d }, // numpad5 - { 0x0000004d, 0x0007005e }, // numpad6 - { 0x00000047, 0x0007005f }, // numpad7 - { 0x00000048, 0x00070060 }, // numpad8 - { 0x00000049, 0x00070061 }, // numpad9 - { 0x00000052, 0x00070062 }, // numpad0 - { 0x00000053, 0x00070063 }, // numpadDecimal - { 0x00000056, 0x00070064 }, // intlBackslash - { 0x0000e05d, 0x00070065 }, // contextMenu - { 0x0000e05e, 0x00070066 }, // power - { 0x00000059, 0x00070067 }, // numpadEqual - { 0x00000064, 0x00070068 }, // f13 - { 0x00000065, 0x00070069 }, // f14 - { 0x00000066, 0x0007006a }, // f15 - { 0x00000067, 0x0007006b }, // f16 - { 0x00000068, 0x0007006c }, // f17 - { 0x00000069, 0x0007006d }, // f18 - { 0x0000006a, 0x0007006e }, // f19 - { 0x0000006b, 0x0007006f }, // f20 - { 0x0000006c, 0x00070070 }, // f21 - { 0x0000006d, 0x00070071 }, // f22 - { 0x0000006e, 0x00070072 }, // f23 - { 0x00000076, 0x00070073 }, // f24 - { 0x0000e03b, 0x00070075 }, // help - { 0x0000e008, 0x0007007a }, // undo - { 0x0000e017, 0x0007007b }, // cut - { 0x0000e018, 0x0007007c }, // copy - { 0x0000e00a, 0x0007007d }, // paste - { 0x0000e020, 0x0007007f }, // audioVolumeMute - { 0x0000e030, 0x00070080 }, // audioVolumeUp - { 0x0000e02e, 0x00070081 }, // audioVolumeDown - { 0x0000007e, 0x00070085 }, // numpadComma - { 0x00000073, 0x00070087 }, // intlRo - { 0x00000070, 0x00070088 }, // kanaMode - { 0x0000007d, 0x00070089 }, // intlYen - { 0x00000079, 0x0007008a }, // convert - { 0x0000007b, 0x0007008b }, // nonConvert - { 0x00000072, 0x00070090 }, // lang1 - { 0x00000071, 0x00070091 }, // lang2 - { 0x00000078, 0x00070092 }, // lang3 - { 0x00000077, 0x00070093 }, // lang4 - { 0x0000001d, 0x000700e0 }, // controlLeft - { 0x0000002a, 0x000700e1 }, // shiftLeft - { 0x00000038, 0x000700e2 }, // altLeft - { 0x0000e05b, 0x000700e3 }, // metaLeft - { 0x0000e01d, 0x000700e4 }, // controlRight - { 0x00000036, 0x000700e5 }, // shiftRight - { 0x0000e038, 0x000700e6 }, // altRight - { 0x0000e05c, 0x000700e7 }, // metaRight - { 0x0000e019, 0x000c00b5 }, // mediaTrackNext - { 0x0000e010, 0x000c00b6 }, // mediaTrackPrevious - { 0x0000e024, 0x000c00b7 }, // mediaStop - { 0x0000e02c, 0x000c00b8 }, // eject - { 0x0000e022, 0x000c00cd }, // mediaPlayPause - { 0x0000e06d, 0x000c0183 }, // mediaSelect - { 0x0000e06c, 0x000c018a }, // launchMail - { 0x0000e021, 0x000c0192 }, // launchApp2 - { 0x0000e06b, 0x000c0194 }, // launchApp1 - { 0x0000e065, 0x000c0221 }, // browserSearch - { 0x0000e032, 0x000c0223 }, // browserHome - { 0x0000e06a, 0x000c0224 }, // browserBack - { 0x0000e069, 0x000c0225 }, // browserForward - { 0x0000e068, 0x000c0226 }, // browserStop - { 0x0000e067, 0x000c0227 }, // browserRefresh - { 0x0000e066, 0x000c022a }, // browserFavorites + {0x0000e05f, 0x00010082}, // sleep + {0x0000e063, 0x00010083}, // wakeUp + {0x000000ff, 0x00070001}, // usbErrorRollOver + {0x000000fc, 0x00070002}, // usbPostFail + {0x0000001e, 0x00070004}, // keyA + {0x00000030, 0x00070005}, // keyB + {0x0000002e, 0x00070006}, // keyC + {0x00000020, 0x00070007}, // keyD + {0x00000012, 0x00070008}, // keyE + {0x00000021, 0x00070009}, // keyF + {0x00000022, 0x0007000a}, // keyG + {0x00000023, 0x0007000b}, // keyH + {0x00000017, 0x0007000c}, // keyI + {0x00000024, 0x0007000d}, // keyJ + {0x00000025, 0x0007000e}, // keyK + {0x00000026, 0x0007000f}, // keyL + {0x00000032, 0x00070010}, // keyM + {0x00000031, 0x00070011}, // keyN + {0x00000018, 0x00070012}, // keyO + {0x00000019, 0x00070013}, // keyP + {0x00000010, 0x00070014}, // keyQ + {0x00000013, 0x00070015}, // keyR + {0x0000001f, 0x00070016}, // keyS + {0x00000014, 0x00070017}, // keyT + {0x00000016, 0x00070018}, // keyU + {0x0000002f, 0x00070019}, // keyV + {0x00000011, 0x0007001a}, // keyW + {0x0000002d, 0x0007001b}, // keyX + {0x00000015, 0x0007001c}, // keyY + {0x0000002c, 0x0007001d}, // keyZ + {0x00000002, 0x0007001e}, // digit1 + {0x00000003, 0x0007001f}, // digit2 + {0x00000004, 0x00070020}, // digit3 + {0x00000005, 0x00070021}, // digit4 + {0x00000006, 0x00070022}, // digit5 + {0x00000007, 0x00070023}, // digit6 + {0x00000008, 0x00070024}, // digit7 + {0x00000009, 0x00070025}, // digit8 + {0x0000000a, 0x00070026}, // digit9 + {0x0000000b, 0x00070027}, // digit0 + {0x0000001c, 0x00070028}, // enter + {0x00000001, 0x00070029}, // escape + {0x0000000e, 0x0007002a}, // backspace + {0x0000000f, 0x0007002b}, // tab + {0x00000039, 0x0007002c}, // space + {0x0000000c, 0x0007002d}, // minus + {0x0000000d, 0x0007002e}, // equal + {0x0000001a, 0x0007002f}, // bracketLeft + {0x0000001b, 0x00070030}, // bracketRight + {0x0000002b, 0x00070031}, // backslash + {0x00000027, 0x00070033}, // semicolon + {0x00000028, 0x00070034}, // quote + {0x00000029, 0x00070035}, // backquote + {0x00000033, 0x00070036}, // comma + {0x00000034, 0x00070037}, // period + {0x00000035, 0x00070038}, // slash + {0x0000003a, 0x00070039}, // capsLock + {0x0000003b, 0x0007003a}, // f1 + {0x0000003c, 0x0007003b}, // f2 + {0x0000003d, 0x0007003c}, // f3 + {0x0000003e, 0x0007003d}, // f4 + {0x0000003f, 0x0007003e}, // f5 + {0x00000040, 0x0007003f}, // f6 + {0x00000041, 0x00070040}, // f7 + {0x00000042, 0x00070041}, // f8 + {0x00000043, 0x00070042}, // f9 + {0x00000044, 0x00070043}, // f10 + {0x00000057, 0x00070044}, // f11 + {0x00000058, 0x00070045}, // f12 + {0x0000e037, 0x00070046}, // printScreen + {0x00000046, 0x00070047}, // scrollLock + {0x00000045, 0x00070048}, // pause + {0x0000e052, 0x00070049}, // insert + {0x0000e047, 0x0007004a}, // home + {0x0000e049, 0x0007004b}, // pageUp + {0x0000e053, 0x0007004c}, // delete + {0x0000e04f, 0x0007004d}, // end + {0x0000e051, 0x0007004e}, // pageDown + {0x0000e04d, 0x0007004f}, // arrowRight + {0x0000e04b, 0x00070050}, // arrowLeft + {0x0000e050, 0x00070051}, // arrowDown + {0x0000e048, 0x00070052}, // arrowUp + {0x0000e045, 0x00070053}, // numLock + {0x0000e035, 0x00070054}, // numpadDivide + {0x00000037, 0x00070055}, // numpadMultiply + {0x0000004a, 0x00070056}, // numpadSubtract + {0x0000004e, 0x00070057}, // numpadAdd + {0x0000e01c, 0x00070058}, // numpadEnter + {0x0000004f, 0x00070059}, // numpad1 + {0x00000050, 0x0007005a}, // numpad2 + {0x00000051, 0x0007005b}, // numpad3 + {0x0000004b, 0x0007005c}, // numpad4 + {0x0000004c, 0x0007005d}, // numpad5 + {0x0000004d, 0x0007005e}, // numpad6 + {0x00000047, 0x0007005f}, // numpad7 + {0x00000048, 0x00070060}, // numpad8 + {0x00000049, 0x00070061}, // numpad9 + {0x00000052, 0x00070062}, // numpad0 + {0x00000053, 0x00070063}, // numpadDecimal + {0x00000056, 0x00070064}, // intlBackslash + {0x0000e05d, 0x00070065}, // contextMenu + {0x0000e05e, 0x00070066}, // power + {0x00000059, 0x00070067}, // numpadEqual + {0x00000064, 0x00070068}, // f13 + {0x00000065, 0x00070069}, // f14 + {0x00000066, 0x0007006a}, // f15 + {0x00000067, 0x0007006b}, // f16 + {0x00000068, 0x0007006c}, // f17 + {0x00000069, 0x0007006d}, // f18 + {0x0000006a, 0x0007006e}, // f19 + {0x0000006b, 0x0007006f}, // f20 + {0x0000006c, 0x00070070}, // f21 + {0x0000006d, 0x00070071}, // f22 + {0x0000006e, 0x00070072}, // f23 + {0x00000076, 0x00070073}, // f24 + {0x0000e03b, 0x00070075}, // help + {0x0000e008, 0x0007007a}, // undo + {0x0000e017, 0x0007007b}, // cut + {0x0000e018, 0x0007007c}, // copy + {0x0000e00a, 0x0007007d}, // paste + {0x0000e020, 0x0007007f}, // audioVolumeMute + {0x0000e030, 0x00070080}, // audioVolumeUp + {0x0000e02e, 0x00070081}, // audioVolumeDown + {0x0000007e, 0x00070085}, // numpadComma + {0x00000073, 0x00070087}, // intlRo + {0x00000070, 0x00070088}, // kanaMode + {0x0000007d, 0x00070089}, // intlYen + {0x00000079, 0x0007008a}, // convert + {0x0000007b, 0x0007008b}, // nonConvert + {0x00000072, 0x00070090}, // lang1 + {0x00000071, 0x00070091}, // lang2 + {0x00000078, 0x00070092}, // lang3 + {0x00000077, 0x00070093}, // lang4 + {0x0000001d, 0x000700e0}, // controlLeft + {0x0000002a, 0x000700e1}, // shiftLeft + {0x00000038, 0x000700e2}, // altLeft + {0x0000e05b, 0x000700e3}, // metaLeft + {0x0000e01d, 0x000700e4}, // controlRight + {0x00000036, 0x000700e5}, // shiftRight + {0x0000e038, 0x000700e6}, // altRight + {0x0000e05c, 0x000700e7}, // metaRight + {0x0000e019, 0x000c00b5}, // mediaTrackNext + {0x0000e010, 0x000c00b6}, // mediaTrackPrevious + {0x0000e024, 0x000c00b7}, // mediaStop + {0x0000e02c, 0x000c00b8}, // eject + {0x0000e022, 0x000c00cd}, // mediaPlayPause + {0x0000e06d, 0x000c0183}, // mediaSelect + {0x0000e06c, 0x000c018a}, // launchMail + {0x0000e021, 0x000c0192}, // launchApp2 + {0x0000e06b, 0x000c0194}, // launchApp1 + {0x0000e065, 0x000c0221}, // browserSearch + {0x0000e032, 0x000c0223}, // browserHome + {0x0000e06a, 0x000c0224}, // browserBack + {0x0000e069, 0x000c0225}, // browserForward + {0x0000e068, 0x000c0226}, // browserStop + {0x0000e067, 0x000c0227}, // browserRefresh + {0x0000e066, 0x000c022a}, // browserFavorites }; std::map FlutterKeyboardManager::windowsToLogicalMap_ = { - { 0x00000008, 0x00000000008 }, // BACK - { 0x00000009, 0x00000000009 }, // TAB - { 0x0000000d, 0x0000000000d }, // RETURN - { 0x0000001b, 0x0000000001b }, // ESCAPE - { 0x00000020, 0x00000000020 }, // SPACE - { 0x000000de, 0x00000000022 }, // OEM_7 - { 0x000000bc, 0x0000000002c }, // OEM_COMMA - { 0x000000bd, 0x0000000002d }, // OEM_MINUS - { 0x000000be, 0x0000000002e }, // OEM_PERIOD - { 0x000000bf, 0x0000000002f }, // OEM_2 - { 0x000000ba, 0x0000000003b }, // OEM_1 - { 0x000000bb, 0x0000000003d }, // OEM_PLUS - { 0x000000db, 0x0000000005b }, // OEM_4 - { 0x000000dc, 0x0000000005c }, // OEM_5 - { 0x000000dd, 0x0000000005d }, // OEM_6 - { 0x000000c0, 0x00000000060 }, // OEM_3 - { 0x0000002e, 0x0000000007f }, // DELETE - { 0x00000014, 0x00000000104 }, // CAPITAL - { 0x00000090, 0x0000000010a }, // NUMLOCK - { 0x00000091, 0x0000000010c }, // SCROLL - { 0x00000028, 0x00000000301 }, // DOWN - { 0x00000025, 0x00000000302 }, // LEFT - { 0x00000027, 0x00000000303 }, // RIGHT - { 0x00000026, 0x00000000304 }, // UP - { 0x00000023, 0x00000000305 }, // END - { 0x00000024, 0x00000000306 }, // HOME - { 0x00000022, 0x00000000307 }, // NEXT - { 0x00000021, 0x00000000308 }, // PRIOR - { 0x0000000c, 0x00000000401 }, // CLEAR - { 0x0000002d, 0x00000000407 }, // INSERT - { 0x0000001e, 0x00000000501 }, // ACCEPT - { 0x000000f6, 0x00000000503 }, // ATTN - { 0x00000003, 0x00000000504 }, // CANCEL - { 0x0000005d, 0x00000000505 }, // APPS - { 0x0000002b, 0x00000000506 }, // EXECUTE - { 0x0000002f, 0x00000000508 }, // HELP - { 0x00000013, 0x00000000509 }, // PAUSE - { 0x000000fa, 0x0000000050a }, // PLAY - { 0x00000029, 0x0000000050c }, // SELECT - { 0x0000001c, 0x00000000705 }, // CONVERT - { 0x0000001f, 0x0000000070b }, // MODECHANGE - { 0x00000070, 0x00000000801 }, // F1 - { 0x00000071, 0x00000000802 }, // F2 - { 0x00000072, 0x00000000803 }, // F3 - { 0x00000073, 0x00000000804 }, // F4 - { 0x00000074, 0x00000000805 }, // F5 - { 0x00000075, 0x00000000806 }, // F6 - { 0x00000076, 0x00000000807 }, // F7 - { 0x00000077, 0x00000000808 }, // F8 - { 0x00000078, 0x00000000809 }, // F9 - { 0x00000079, 0x0000000080a }, // F10 - { 0x0000007a, 0x0000000080b }, // F11 - { 0x0000007b, 0x0000000080c }, // F12 - { 0x0000007c, 0x0000000080d }, // F13 - { 0x0000007d, 0x0000000080e }, // F14 - { 0x0000007e, 0x0000000080f }, // F15 - { 0x0000007f, 0x00000000810 }, // F16 - { 0x00000080, 0x00000000811 }, // F17 - { 0x00000081, 0x00000000812 }, // F18 - { 0x00000082, 0x00000000813 }, // F19 - { 0x00000083, 0x00000000814 }, // F20 - { 0x00000084, 0x00000000815 }, // F21 - { 0x00000085, 0x00000000816 }, // F22 - { 0x00000086, 0x00000000817 }, // F23 - { 0x00000087, 0x00000000818 }, // F24 - { 0x000000b3, 0x00000000a05 }, // MEDIA_PLAY_PAUSE - { 0x000000b2, 0x00000000a07 }, // MEDIA_STOP - { 0x0000002a, 0x00000000a0c }, // PRINT - { 0x000000ae, 0x00000000a0f }, // VOLUME_DOWN - { 0x000000af, 0x00000000a10 }, // VOLUME_UP - { 0x000000ad, 0x00000000a11 }, // VOLUME_MUTE - { 0x000000b4, 0x00000000b03 }, // LAUNCH_MAIL - { 0x000000a6, 0x00000000c01 }, // BROWSER_BACK - { 0x000000ab, 0x00000000c02 }, // BROWSER_FAVORITES - { 0x000000a7, 0x00000000c03 }, // BROWSER_FORWARD - { 0x000000ac, 0x00000000c04 }, // BROWSER_HOME - { 0x000000a8, 0x00000000c05 }, // BROWSER_REFRESH - { 0x000000aa, 0x00000000c06 }, // BROWSER_SEARCH - { 0x000000a9, 0x00000000c07 }, // BROWSER_STOP - { 0x000000c3, 0x0000005ff08 }, // GAMEPAD_A - { 0x000000c4, 0x0000005ff09 }, // GAMEPAD_B - { 0x000000c5, 0x0000005ff0a }, // GAMEPAD_X - { 0x000000c6, 0x0000005ff0b }, // GAMEPAD_Y - { 0x000000c7, 0x0000005ff0c }, // GAMEPAD_RIGHT_SHOULDER - { 0x000000c8, 0x0000005ff0d }, // GAMEPAD_LEFT_SHOULDER - { 0x000000c9, 0x0000005ff0e }, // GAMEPAD_LEFT_TRIGGER - { 0x000000ca, 0x0000005ff0f }, // GAMEPAD_RIGHT_TRIGGER - { 0x000000cb, 0x0000005ff10 }, // GAMEPAD_DPAD_UP - { 0x0000005f, 0x00100010082 }, // SLEEP - { 0x00000015, 0x00100070090 }, // KANA - { 0x00000015, 0x00100070090 }, // HANGEUL - { 0x00000015, 0x00100070090 }, // HANGUL - { 0x0000006a, 0x0020000002a }, // MULTIPLY - { 0x0000006b, 0x0020000002b }, // ADD - { 0x0000006d, 0x0020000002d }, // SUBTRACT - { 0x0000006e, 0x0020000002e }, // DECIMAL - { 0x0000006f, 0x0020000002f }, // DIVIDE - { 0x00000060, 0x00200000030 }, // NUMPAD0 - { 0x00000061, 0x00200000031 }, // NUMPAD1 - { 0x00000062, 0x00200000032 }, // NUMPAD2 - { 0x00000063, 0x00200000033 }, // NUMPAD3 - { 0x00000064, 0x00200000034 }, // NUMPAD4 - { 0x00000065, 0x00200000035 }, // NUMPAD5 - { 0x00000066, 0x00200000036 }, // NUMPAD6 - { 0x00000067, 0x00200000037 }, // NUMPAD7 - { 0x00000068, 0x00200000038 }, // NUMPAD8 - { 0x00000069, 0x00200000039 }, // NUMPAD9 - { 0x00000092, 0x0020000003d }, // OEM_NEC_EQUAL - { 0x000000a4, 0x00300000102 }, // LMENU - { 0x00000011, 0x00300000105 }, // CONTROL - { 0x000000a2, 0x00300000105 }, // LCONTROL - { 0x0000005b, 0x00300000109 }, // LWIN - { 0x00000010, 0x0030000010d }, // SHIFT - { 0x000000a0, 0x0030000010d }, // LSHIFT - { 0x000000a5, 0x00400000102 }, // RMENU - { 0x000000a3, 0x00400000105 }, // RCONTROL - { 0x0000005c, 0x00400000109 }, // RWIN - { 0x000000a1, 0x0040000010d }, // RSHIFT + {0x00000008, 0x00000000008}, // BACK + {0x00000009, 0x00000000009}, // TAB + {0x0000000d, 0x0000000000d}, // RETURN + {0x0000001b, 0x0000000001b}, // ESCAPE + {0x00000020, 0x00000000020}, // SPACE + {0x000000de, 0x00000000022}, // OEM_7 + {0x000000bc, 0x0000000002c}, // OEM_COMMA + {0x000000bd, 0x0000000002d}, // OEM_MINUS + {0x000000be, 0x0000000002e}, // OEM_PERIOD + {0x000000bf, 0x0000000002f}, // OEM_2 + {0x000000ba, 0x0000000003b}, // OEM_1 + {0x000000bb, 0x0000000003d}, // OEM_PLUS + {0x000000db, 0x0000000005b}, // OEM_4 + {0x000000dc, 0x0000000005c}, // OEM_5 + {0x000000dd, 0x0000000005d}, // OEM_6 + {0x000000c0, 0x00000000060}, // OEM_3 + {0x0000002e, 0x0000000007f}, // DELETE + {0x00000014, 0x00000000104}, // CAPITAL + {0x00000090, 0x0000000010a}, // NUMLOCK + {0x00000091, 0x0000000010c}, // SCROLL + {0x00000028, 0x00000000301}, // DOWN + {0x00000025, 0x00000000302}, // LEFT + {0x00000027, 0x00000000303}, // RIGHT + {0x00000026, 0x00000000304}, // UP + {0x00000023, 0x00000000305}, // END + {0x00000024, 0x00000000306}, // HOME + {0x00000022, 0x00000000307}, // NEXT + {0x00000021, 0x00000000308}, // PRIOR + {0x0000000c, 0x00000000401}, // CLEAR + {0x0000002d, 0x00000000407}, // INSERT + {0x0000001e, 0x00000000501}, // ACCEPT + {0x000000f6, 0x00000000503}, // ATTN + {0x00000003, 0x00000000504}, // CANCEL + {0x0000005d, 0x00000000505}, // APPS + {0x0000002b, 0x00000000506}, // EXECUTE + {0x0000002f, 0x00000000508}, // HELP + {0x00000013, 0x00000000509}, // PAUSE + {0x000000fa, 0x0000000050a}, // PLAY + {0x00000029, 0x0000000050c}, // SELECT + {0x0000001c, 0x00000000705}, // CONVERT + {0x0000001f, 0x0000000070b}, // MODECHANGE + {0x00000070, 0x00000000801}, // F1 + {0x00000071, 0x00000000802}, // F2 + {0x00000072, 0x00000000803}, // F3 + {0x00000073, 0x00000000804}, // F4 + {0x00000074, 0x00000000805}, // F5 + {0x00000075, 0x00000000806}, // F6 + {0x00000076, 0x00000000807}, // F7 + {0x00000077, 0x00000000808}, // F8 + {0x00000078, 0x00000000809}, // F9 + {0x00000079, 0x0000000080a}, // F10 + {0x0000007a, 0x0000000080b}, // F11 + {0x0000007b, 0x0000000080c}, // F12 + {0x0000007c, 0x0000000080d}, // F13 + {0x0000007d, 0x0000000080e}, // F14 + {0x0000007e, 0x0000000080f}, // F15 + {0x0000007f, 0x00000000810}, // F16 + {0x00000080, 0x00000000811}, // F17 + {0x00000081, 0x00000000812}, // F18 + {0x00000082, 0x00000000813}, // F19 + {0x00000083, 0x00000000814}, // F20 + {0x00000084, 0x00000000815}, // F21 + {0x00000085, 0x00000000816}, // F22 + {0x00000086, 0x00000000817}, // F23 + {0x00000087, 0x00000000818}, // F24 + {0x000000b3, 0x00000000a05}, // MEDIA_PLAY_PAUSE + {0x000000b2, 0x00000000a07}, // MEDIA_STOP + {0x0000002a, 0x00000000a0c}, // PRINT + {0x000000ae, 0x00000000a0f}, // VOLUME_DOWN + {0x000000af, 0x00000000a10}, // VOLUME_UP + {0x000000ad, 0x00000000a11}, // VOLUME_MUTE + {0x000000b4, 0x00000000b03}, // LAUNCH_MAIL + {0x000000a6, 0x00000000c01}, // BROWSER_BACK + {0x000000ab, 0x00000000c02}, // BROWSER_FAVORITES + {0x000000a7, 0x00000000c03}, // BROWSER_FORWARD + {0x000000ac, 0x00000000c04}, // BROWSER_HOME + {0x000000a8, 0x00000000c05}, // BROWSER_REFRESH + {0x000000aa, 0x00000000c06}, // BROWSER_SEARCH + {0x000000a9, 0x00000000c07}, // BROWSER_STOP + {0x000000c3, 0x0000005ff08}, // GAMEPAD_A + {0x000000c4, 0x0000005ff09}, // GAMEPAD_B + {0x000000c5, 0x0000005ff0a}, // GAMEPAD_X + {0x000000c6, 0x0000005ff0b}, // GAMEPAD_Y + {0x000000c7, 0x0000005ff0c}, // GAMEPAD_RIGHT_SHOULDER + {0x000000c8, 0x0000005ff0d}, // GAMEPAD_LEFT_SHOULDER + {0x000000c9, 0x0000005ff0e}, // GAMEPAD_LEFT_TRIGGER + {0x000000ca, 0x0000005ff0f}, // GAMEPAD_RIGHT_TRIGGER + {0x000000cb, 0x0000005ff10}, // GAMEPAD_DPAD_UP + {0x0000005f, 0x00100010082}, // SLEEP + {0x00000015, 0x00100070090}, // KANA + {0x00000015, 0x00100070090}, // HANGEUL + {0x00000015, 0x00100070090}, // HANGUL + {0x0000006a, 0x0020000002a}, // MULTIPLY + {0x0000006b, 0x0020000002b}, // ADD + {0x0000006d, 0x0020000002d}, // SUBTRACT + {0x0000006e, 0x0020000002e}, // DECIMAL + {0x0000006f, 0x0020000002f}, // DIVIDE + {0x00000060, 0x00200000030}, // NUMPAD0 + {0x00000061, 0x00200000031}, // NUMPAD1 + {0x00000062, 0x00200000032}, // NUMPAD2 + {0x00000063, 0x00200000033}, // NUMPAD3 + {0x00000064, 0x00200000034}, // NUMPAD4 + {0x00000065, 0x00200000035}, // NUMPAD5 + {0x00000066, 0x00200000036}, // NUMPAD6 + {0x00000067, 0x00200000037}, // NUMPAD7 + {0x00000068, 0x00200000038}, // NUMPAD8 + {0x00000069, 0x00200000039}, // NUMPAD9 + {0x00000092, 0x0020000003d}, // OEM_NEC_EQUAL + {0x000000a4, 0x00300000102}, // LMENU + {0x00000011, 0x00300000105}, // CONTROL + {0x000000a2, 0x00300000105}, // LCONTROL + {0x0000005b, 0x00300000109}, // LWIN + {0x00000010, 0x0030000010d}, // SHIFT + {0x000000a0, 0x0030000010d}, // LSHIFT + {0x000000a5, 0x00400000102}, // RMENU + {0x000000a3, 0x00400000105}, // RCONTROL + {0x0000005c, 0x00400000109}, // RWIN + {0x000000a1, 0x0040000010d}, // RSHIFT }; std::map FlutterKeyboardManager::scanCodeToLogicalMap_ = { - { 0x0000e01d, 0x0400000105 }, // ControlRight - { 0x0000e038, 0x0400000102 }, // AltRight - { 0x0000004f, 0x0200000031 }, // Numpad1 - { 0x00000050, 0x0200000032 }, // Numpad2 - { 0x00000051, 0x0200000033 }, // Numpad3 - { 0x0000004b, 0x0200000034 }, // Numpad4 - { 0x0000004c, 0x0200000035 }, // Numpad5 - { 0x0000004d, 0x0200000036 }, // Numpad6 - { 0x00000047, 0x0200000037 }, // Numpad7 - { 0x00000048, 0x0200000038 }, // Numpad8 - { 0x00000049, 0x0200000039 }, // Numpad9 - { 0x00000052, 0x0200000030 }, // Numpad0 - { 0x0000004e, 0x020000002b }, // NumpadAdd - { 0x00000053, 0x020000002e }, // NumpadDecimal - { 0x0000e035, 0x020000002f }, // NumpadDivide - { 0x00000059, 0x020000003d }, // NumpadEqual - { 0x00000037, 0x020000002a }, // NumpadMultiply - { 0x0000004a, 0x020000002d }, // NumpadSubtract + {0x0000e01d, 0x0400000105}, // ControlRight + {0x0000e038, 0x0400000102}, // AltRight + {0x0000004f, 0x0200000031}, // Numpad1 + {0x00000050, 0x0200000032}, // Numpad2 + {0x00000051, 0x0200000033}, // Numpad3 + {0x0000004b, 0x0200000034}, // Numpad4 + {0x0000004c, 0x0200000035}, // Numpad5 + {0x0000004d, 0x0200000036}, // Numpad6 + {0x00000047, 0x0200000037}, // Numpad7 + {0x00000048, 0x0200000038}, // Numpad8 + {0x00000049, 0x0200000039}, // Numpad9 + {0x00000052, 0x0200000030}, // Numpad0 + {0x0000004e, 0x020000002b}, // NumpadAdd + {0x00000053, 0x020000002e}, // NumpadDecimal + {0x0000e035, 0x020000002f}, // NumpadDivide + {0x00000059, 0x020000003d}, // NumpadEqual + {0x00000037, 0x020000002a}, // NumpadMultiply + {0x0000004a, 0x020000002d}, // NumpadSubtract }; } From 0af761d6a6c28bffbfbd58aa863d216ab1c540cc Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 16:47:57 -0800 Subject: [PATCH 06/82] Clang format --- lib/ui/window/key_data_packet.cc | 6 +-- lib/ui/window/key_data_packet.h | 3 +- lib/ui/window/window.cc | 4 +- lib/ui/window/window.h | 2 +- runtime/runtime_controller.cc | 7 ++- shell/common/engine.cc | 3 +- shell/common/platform_view.h | 8 ++-- shell/common/shell.cc | 4 +- shell/platform/embedder/embedder.cc | 16 ++++--- shell/platform/embedder/embedder.h | 2 - shell/platform/embedder/embedder_engine.h | 3 +- shell/platform/windows/flutter_key_map.cc | 11 +++-- .../windows/flutter_keyboard_manager.cc | 30 ++++++------ .../windows/flutter_keyboard_manager.h | 5 +- .../flutter_keyboard_manager_unittests.cc | 47 ++++++++++--------- .../platform/windows/flutter_windows_view.cc | 2 +- shell/platform/windows/flutter_windows_view.h | 12 ++++- shell/platform/windows/win32_flutter_window.h | 6 ++- shell/platform/windows/win32_window.cc | 3 +- shell/platform/windows/win32_window.h | 6 ++- .../windows/window_binding_handler_delegate.h | 6 ++- 21 files changed, 107 insertions(+), 79 deletions(-) diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc index 6bcc0a6e08674..0a22b04f13d9d 100644 --- a/lib/ui/window/key_data_packet.cc +++ b/lib/ui/window/key_data_packet.cc @@ -10,8 +10,7 @@ namespace flutter { KeyDataPacket::KeyDataPacket(uint8_t* data, size_t num_bytes) : data_(data, data + num_bytes) {} -KeyDataPacket::KeyDataPacket(size_t num_bytes) - : data_(num_bytes) {} +KeyDataPacket::KeyDataPacket(size_t num_bytes) : data_(num_bytes) {} KeyDataPacket::~KeyDataPacket() = default; @@ -29,7 +28,8 @@ void KeyDataPacketBuilder::SetKeyData(const KeyData& event) { void KeyDataPacketBuilder::SetCharacter(const char* character) { if (character != nullptr) { - memcpy(data().data() + CharacterStart_(), character, data().size() - CharacterStart_()); + memcpy(data().data() + CharacterStart_(), character, + data().size() - CharacterStart_()); } } diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h index 1f3e55baec214..fa48a60f7b676 100644 --- a/lib/ui/window/key_data_packet.h +++ b/lib/ui/window/key_data_packet.h @@ -11,11 +11,10 @@ #include "flutter/fml/macros.h" #include "flutter/lib/ui/window/key_data.h" - namespace flutter { // Bitstream that contains a KeyData. -// +// // This class provides interface to read the opaque bits. For constructing such // a bitstream from KeyData, checkout KeyDataPacketBuilder. class KeyDataPacket { diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 51a4c29724af4..a6cbf7f35a40c 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -48,8 +48,8 @@ void Window::DispatchKeyDataPacket(const KeyDataPacket& packet) { if (Dart_IsError(data_handle)) { return; } - tonic::LogIfError(tonic::DartInvokeField( - library_.value(), "_dispatchKeyData", {data_handle})); + tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_dispatchKeyData", + {data_handle})); } void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index 7080addeff6ef..d81e7e95aaa86 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -9,8 +9,8 @@ #include #include -#include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/key_data_packet.h" +#include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/viewport_metrics.h" #include "third_party/skia/include/gpu/GrDirectContext.h" diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 6fe32fbbf2de6..b8a0327a393a9 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -243,11 +243,10 @@ bool RuntimeController::DispatchPointerDataPacket( return false; } -bool RuntimeController::DispatchKeyDataPacket( - const KeyDataPacket& packet) { +bool RuntimeController::DispatchKeyDataPacket(const KeyDataPacket& packet) { if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { - TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataPacket", - "mode", "basic"); + TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataPacket", "mode", + "basic"); platform_configuration->get_window(0)->DispatchKeyDataPacket(packet); return true; } diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 508c5c0bcf563..6b49eab9badcd 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -384,8 +384,7 @@ void Engine::DispatchPointerDataPacket( pointer_data_dispatcher_->DispatchPacket(std::move(packet), trace_flow_id); } -void Engine::DispatchKeyDataPacket( - std::unique_ptr packet) { +void Engine::DispatchKeyDataPacket(std::unique_ptr packet) { TRACE_EVENT0("flutter", "Engine::DispatchKeyDataPacket"); if (runtime_controller_) { runtime_controller_->DispatchKeyDataPacket(*packet); diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 21b98be140d09..d8beccd37d6b1 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -132,7 +132,8 @@ class PlatformView { /// the running root isolate hosted by the engine on the UI /// thread. /// - /// @param[in] packet The key data packet containing one physical key event + /// @param[in] packet The key data packet containing one physical key + /// event /// and multiple logical key events. /// virtual void OnPlatformViewDispatchKeyDataPacket( @@ -590,8 +591,9 @@ class PlatformView { //---------------------------------------------------------------------------- /// @brief Dispatches key events from the embedder to the framework. Each - /// key data packet contains one physical event and multiple logical key - /// events. Each call to this method wakes up the UI thread. + /// key data packet contains one physical event and multiple + /// logical key events. Each call to this method wakes up the UI + /// thread. /// /// @param[in] packet The key data packet to dispatch to the framework. /// diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 8ec6525470f81..db0938ed8ce0b 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -882,8 +882,8 @@ void Shell::OnPlatformViewDispatchKeyDataPacket( FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - task_runners_.GetUITaskRunner()->PostTask( - fml::MakeCopyable([engine = weak_engine_, packet = std::move(packet)]() mutable { + task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( + [engine = weak_engine_, packet = std::move(packet)]() mutable { if (engine) { engine->DispatchKeyDataPacket(std::move(packet)); } diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index ff1928be7456f..f580eb8e67fc8 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1514,9 +1514,9 @@ inline flutter::KeyChange ToKeyChange(FlutterKeyEventKind key_change) { // i.e. 8 bytes. constexpr size_t kKeyEventCharacterMaxBytes = 8; -FlutterEngineResult FlutterEngineSendKeyEvent( - FLUTTER_API_SYMBOL(FlutterEngine) engine, - const FlutterKeyEvent* event) { +FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) + engine, + const FlutterKeyEvent* event) { if (engine == nullptr) { return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); } @@ -1527,15 +1527,17 @@ FlutterEngineResult FlutterEngineSendKeyEvent( const char* character = SAFE_ACCESS(event, character, nullptr); - size_t character_data_size = character == nullptr ? 0 : - strnlen(character, kKeyEventCharacterMaxBytes); + size_t character_data_size = + character == nullptr ? 0 : strnlen(character, kKeyEventCharacterMaxBytes); - auto packet = std::make_unique(character_data_size); + auto packet = + std::make_unique(character_data_size); flutter::KeyData key_data; key_data.Clear(); key_data.timestamp = (uint64_t)SAFE_ACCESS(event, timestamp, 0); - key_data.change = ToKeyChange(SAFE_ACCESS(event, kind, FlutterKeyEventKind::kFlutterKeyEventKindUp)); + key_data.change = ToKeyChange( + SAFE_ACCESS(event, kind, FlutterKeyEventKind::kFlutterKeyEventKindUp)); key_data.physical = SAFE_ACCESS(event, physical, 0); key_data.logical = SAFE_ACCESS(event, logical, 0); key_data.synthesized = !!SAFE_ACCESS(event, synthesized, false); diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index ec54b5f3e18f8..9f02e3cabebf3 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -611,7 +611,6 @@ typedef struct { int64_t buttons; } FlutterPointerEvent; - typedef enum { kFlutterKeyEventKindUp = 1, kFlutterKeyEventKindDown, @@ -642,7 +641,6 @@ struct _FlutterPlatformKeyEventResponseHandle; typedef struct _FlutterPlatformKeyEventResponseHandle FlutterPlatformKeyEventResponseHandle; - struct _FlutterPlatformMessageResponseHandle; typedef struct _FlutterPlatformMessageResponseHandle FlutterPlatformMessageResponseHandle; diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index fe044e29eecb8..53a7b315c219a 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -60,8 +60,7 @@ class EmbedderEngine { bool DispatchPointerDataPacket( std::unique_ptr packet); - bool DispatchKeyDataPacket( - std::unique_ptr packet); + bool DispatchKeyDataPacket(std::unique_ptr packet); bool SendPlatformMessage(fml::RefPtr message); diff --git a/shell/platform/windows/flutter_key_map.cc b/shell/platform/windows/flutter_key_map.cc index bac421a3918d0..be3df5ef8e5f8 100644 --- a/shell/platform/windows/flutter_key_map.cc +++ b/shell/platform/windows/flutter_key_map.cc @@ -10,11 +10,12 @@ #include // DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT -// This file is generated by flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and -// should not be edited directly. +// This file is generated by +// flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and should not +// be edited directly. // -// Edit the template dev/tools/gen_keycodes/data/windows_flutter_key_map_cc.tmpl instead. -// See dev/tools/gen_keycodes/README.md for more information. +// Edit the template dev/tools/gen_keycodes/data/windows_flutter_key_map_cc.tmpl +// instead. See dev/tools/gen_keycodes/README.md for more information. namespace flutter { @@ -320,6 +321,6 @@ std::map FlutterKeyboardManager::scanCodeToLogicalMap_ = { {0x0000004a, 0x020000002d}, // NumpadSubtract }; -} +} // namespace flutter #endif diff --git a/shell/platform/windows/flutter_keyboard_manager.cc b/shell/platform/windows/flutter_keyboard_manager.cc index dd86e3cface70..9cc1e59a2c4c6 100644 --- a/shell/platform/windows/flutter_keyboard_manager.cc +++ b/shell/platform/windows/flutter_keyboard_manager.cc @@ -4,20 +4,19 @@ #include "flutter/shell/platform/windows/flutter_keyboard_manager.h" -#include #include +#include #include +#include #include #include -#include #include "flutter/shell/platform/windows/string_conversion.h" namespace flutter { -namespace { -} // namespace +namespace {} // namespace /** * The code prefix for keys which do not have a Unicode representation. @@ -40,26 +39,28 @@ constexpr uint64_t kWindowsKeyIdPlane = 0x00700000000; /** * Mask for the auto-generated bit portion of the key code. - * + * * This is used by platform-specific code to generate new Flutter key codes * for keys which are not recognized. */ constexpr uint64_t kAutogeneratedMask = 0x10000000000; -FlutterKeyboardManager::FlutterKeyboardManager(std::function onEvent) +FlutterKeyboardManager::FlutterKeyboardManager( + std::function onEvent) : onEvent_(onEvent) {} FlutterKeyboardManager::~FlutterKeyboardManager() = default; void FlutterKeyboardManager::TextHook(FlutterWindowsView* view, - const std::u16string& code_point) {} + const std::u16string& code_point) {} static bool isAsciiPrintable(int codeUnit) { return codeUnit <= 0x7f && codeUnit >= 0x20; } static bool isControlCharacter(int codeUnit) { - return (codeUnit <= 0x1f && codeUnit >= 0x00) || (codeUnit >= 0x7f && codeUnit <= 0x9f); + return (codeUnit <= 0x1f && codeUnit >= 0x00) || + (codeUnit >= 0x7f && codeUnit <= 0x9f); } // Transform scancodes sent by windows to scancodes written in Chromium spec. @@ -136,7 +137,8 @@ void FlutterKeyboardManager::KeyboardHook(FlutterWindowsView* view, auto last_logical_record_iter = pressingRecords_.find(physical_key); const bool had_record = last_logical_record_iter != pressingRecords_.end(); - const uint64_t last_logical_record = had_record ? last_logical_record_iter->second : 0; + const uint64_t last_logical_record = + had_record ? last_logical_record_iter->second : 0; // The resulting event's `change`. FlutterKeyEventKind change; @@ -156,8 +158,8 @@ void FlutterKeyboardManager::KeyboardHook(FlutterWindowsView* view, next_logical_record = last_logical_record; result_logical_key = last_logical_record; } else { - // A non-repeated key has been pressed that has the exact physical key as - // a currently pressed one, usually indicating multiple keyboards are + // A non-repeated key has been pressed that has the exact physical key + // as a currently pressed one, usually indicating multiple keyboards are // pressing keys with the same physical key, or the up event was lost // during a loss of focus. The down event is ignored. return; @@ -170,7 +172,7 @@ void FlutterKeyboardManager::KeyboardHook(FlutterWindowsView* view, next_logical_record = logical_key; result_logical_key = logical_key; } - } else { // isPhysicalDown is false + } else { // isPhysicalDown is false if (last_logical_record == 0) { // The physical key has been released before. It indicates multiple // keyboards pressed keys with the same physical key. Ignore the up event. @@ -180,7 +182,7 @@ void FlutterKeyboardManager::KeyboardHook(FlutterWindowsView* view, change = kFlutterKeyEventKindUp; assert(had_record); // Up events never have character. - character_cache_[0] = '\0'; + character_cache_[0] = '\0'; next_has_record = false; result_logical_key = last_logical_record; } @@ -202,7 +204,7 @@ void FlutterKeyboardManager::KeyboardHook(FlutterWindowsView* view, FlutterKeyEvent key_data = {}; key_data.struct_size = sizeof(FlutterKeyEvent); - key_data.timestamp = + key_data.timestamp = std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch()) .count(); diff --git a/shell/platform/windows/flutter_keyboard_manager.h b/shell/platform/windows/flutter_keyboard_manager.h index 1d738d9b59a8f..e1a97c24273bb 100644 --- a/shell/platform/windows/flutter_keyboard_manager.h +++ b/shell/platform/windows/flutter_keyboard_manager.h @@ -22,7 +22,7 @@ namespace { // It should hold a UTF-32 character encoded in UTF-8 as long as the trailing // '\0'. constexpr size_t kCharacterCacheSize = 8; -} +} // namespace class FlutterWindowsView; @@ -31,7 +31,8 @@ class FlutterWindowsView; // Handles key events and forwards them to the Flutter engine. class FlutterKeyboardManager : public KeyboardHookHandler { public: - explicit FlutterKeyboardManager(std::function onEvent); + explicit FlutterKeyboardManager( + std::function onEvent); virtual ~FlutterKeyboardManager(); diff --git a/shell/platform/windows/flutter_keyboard_manager_unittests.cc b/shell/platform/windows/flutter_keyboard_manager_unittests.cc index 481ec220a5e79..4c70cf6f909f3 100644 --- a/shell/platform/windows/flutter_keyboard_manager_unittests.cc +++ b/shell/platform/windows/flutter_keyboard_manager_unittests.cc @@ -39,7 +39,7 @@ class TestFlutterKeyEvent : public FlutterKeyEvent { std::unique_ptr character_ptr; }; -} // namespace +} // namespace namespace testing { @@ -52,21 +52,22 @@ constexpr uint64_t kLogicalKeyA = 0x41; constexpr uint64_t kLogicalNumpad1 = 0x61; constexpr uint64_t kLogicalNumpadEnd = 0x23; constexpr uint64_t kLogicalNumLock = 0x90; -} +} // namespace TEST(FlutterKeyboardManager, BasicKeyPressingAndHolding) { std::vector results; TestFlutterKeyEvent* event; - std::unique_ptr manager = std::make_unique( - [&results](const FlutterKeyEvent& event) { - results.emplace_back(event); - } - ); + std::unique_ptr manager = + std::make_unique( + [&results](const FlutterKeyEvent& event) { + results.emplace_back(event); + }); // On a US keyboard: // Press Numpad1. - manager->KeyboardHook(nullptr, kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', false); + manager->KeyboardHook(nullptr, kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', + false); EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->kind, kFlutterKeyEventKindDown); @@ -77,7 +78,8 @@ TEST(FlutterKeyboardManager, BasicKeyPressingAndHolding) { results.clear(); // Hold KeyA. - manager->KeyboardHook(nullptr, kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', true); + manager->KeyboardHook(nullptr, kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', + true); EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->kind, kFlutterKeyEventKindRepeat); @@ -88,7 +90,8 @@ TEST(FlutterKeyboardManager, BasicKeyPressingAndHolding) { results.clear(); // Release KeyA. - manager->KeyboardHook(nullptr, kLogicalKeyA, kPhysicalKeyA, WM_KEYUP, 0, true); + manager->KeyboardHook(nullptr, kLogicalKeyA, kPhysicalKeyA, WM_KEYUP, 0, + true); EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->kind, kFlutterKeyEventKindUp); @@ -96,22 +99,22 @@ TEST(FlutterKeyboardManager, BasicKeyPressingAndHolding) { EXPECT_EQ(event->logical, 0x00000061); EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, false); - } TEST(FlutterKeyboardManager, ToggleNumLockDuringNumpadPress) { std::vector results; TestFlutterKeyEvent* event; - std::unique_ptr manager = std::make_unique( - [&results](const FlutterKeyEvent& event) { - results.emplace_back(event); - } - ); + std::unique_ptr manager = + std::make_unique( + [&results](const FlutterKeyEvent& event) { + results.emplace_back(event); + }); // On a US keyboard: // Press NumPad1. - manager->KeyboardHook(nullptr, kLogicalNumpad1, kPhysicalNumpad1, WM_KEYDOWN, 0, false); + manager->KeyboardHook(nullptr, kLogicalNumpad1, kPhysicalNumpad1, WM_KEYDOWN, + 0, false); EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->kind, kFlutterKeyEventKindDown); @@ -122,7 +125,8 @@ TEST(FlutterKeyboardManager, ToggleNumLockDuringNumpadPress) { results.clear(); // Press NumLock. - manager->KeyboardHook(nullptr, kLogicalNumLock, kPhysicalNumLock, WM_KEYDOWN, 0, false); + manager->KeyboardHook(nullptr, kLogicalNumLock, kPhysicalNumLock, WM_KEYDOWN, + 0, false); EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->kind, kFlutterKeyEventKindDown); @@ -133,7 +137,8 @@ TEST(FlutterKeyboardManager, ToggleNumLockDuringNumpadPress) { results.clear(); // Release NumLock. - manager->KeyboardHook(nullptr, kLogicalNumLock, kPhysicalNumLock, WM_KEYUP, 0, false); + manager->KeyboardHook(nullptr, kLogicalNumLock, kPhysicalNumLock, WM_KEYUP, 0, + false); EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->kind, kFlutterKeyEventKindUp); @@ -144,7 +149,8 @@ TEST(FlutterKeyboardManager, ToggleNumLockDuringNumpadPress) { results.clear(); // Release NumPad1. (The logical key is now NumpadEnd) - manager->KeyboardHook(nullptr, kLogicalNumpadEnd, kPhysicalNumpad1, WM_KEYUP, 0, false); + manager->KeyboardHook(nullptr, kLogicalNumpadEnd, kPhysicalNumpad1, WM_KEYUP, + 0, false); EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->kind, kFlutterKeyEventKindUp); @@ -153,7 +159,6 @@ TEST(FlutterKeyboardManager, ToggleNumLockDuringNumpadPress) { EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, false); results.clear(); - } } // namespace testing diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index e539f5d844393..a8046da3d73ff 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -39,7 +39,7 @@ void FlutterWindowsView::SetEngine( auto internal_plugin_messenger = internal_plugin_registrar_->messenger(); keyboard_hook_handlers_.push_back( std::make_unique( - [this](const FlutterKeyEvent& event){ + [this](const FlutterKeyEvent& event) { engine_->SendKeyEvent(event); })); keyboard_hook_handlers_.push_back( diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 2561659a281a8..4bfb23b9bfd5b 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -89,7 +89,11 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate { void OnText(const std::u16string&) override; // |WindowBindingHandlerDelegate| - void OnKey(int key, int scancode, int action, char32_t character, bool wasDown) override; + void OnKey(int key, + int scancode, + int action, + char32_t character, + bool wasDown) override; // |WindowBindingHandlerDelegate| void OnScroll(double x, @@ -139,7 +143,11 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate { void SendText(const std::u16string&); // Reports a raw keyboard message to Flutter engine. - void SendKey(int key, int scancode, int action, char32_t character, bool wasDown); + void SendKey(int key, + int scancode, + int action, + char32_t character, + bool wasDown); // Reports scroll wheel events to Flutter engine. void SendScroll(double x, diff --git a/shell/platform/windows/win32_flutter_window.h b/shell/platform/windows/win32_flutter_window.h index 3b311afa40422..eae8b7b1faa51 100644 --- a/shell/platform/windows/win32_flutter_window.h +++ b/shell/platform/windows/win32_flutter_window.h @@ -54,7 +54,11 @@ class Win32FlutterWindow : public Win32Window, public WindowBindingHandler { void OnText(const std::u16string& text) override; // |Win32Window| - void OnKey(int key, int scancode, int action, char32_t character, bool wasDown) override; + void OnKey(int key, + int scancode, + int action, + char32_t character, + bool wasDown) override; // |Win32Window| void OnScroll(double delta_x, double delta_y) override; diff --git a/shell/platform/windows/win32_window.cc b/shell/platform/windows/win32_window.cc index 09a946805f57b..89d5c5a4318ab 100644 --- a/shell/platform/windows/win32_window.cc +++ b/shell/platform/windows/win32_window.cc @@ -243,7 +243,8 @@ Win32Window::HandleMessage(UINT const message, if (keycode_for_char_message_ != 0) { const unsigned int scancode = (lparam >> 16) & 0x1ff; const bool wasDown = lparam & 0x40000000; - OnKey(keycode_for_char_message_, scancode, WM_KEYDOWN, code_point, wasDown); + OnKey(keycode_for_char_message_, scancode, WM_KEYDOWN, code_point, + wasDown); keycode_for_char_message_ = 0; } break; diff --git a/shell/platform/windows/win32_window.h b/shell/platform/windows/win32_window.h index ef2176a95799b..26ca4009eaa17 100644 --- a/shell/platform/windows/win32_window.h +++ b/shell/platform/windows/win32_window.h @@ -92,7 +92,11 @@ class Win32Window { virtual void OnText(const std::u16string& text) = 0; // Called when raw keyboard input occurs. - virtual void OnKey(int key, int scancode, int action, char32_t character, bool wasDown) = 0; + virtual void OnKey(int key, + int scancode, + int action, + char32_t character, + bool wasDown) = 0; // Called when mouse scrollwheel input occurs. virtual void OnScroll(double delta_x, double delta_y) = 0; diff --git a/shell/platform/windows/window_binding_handler_delegate.h b/shell/platform/windows/window_binding_handler_delegate.h index 334614c9e09af..98b5b14ee8228 100644 --- a/shell/platform/windows/window_binding_handler_delegate.h +++ b/shell/platform/windows/window_binding_handler_delegate.h @@ -41,7 +41,11 @@ class WindowBindingHandlerDelegate { // Notifies delegate that backing window size has received key press. // Typically called by currently configured WindowBindingHandler - virtual void OnKey(int key, int scancode, int action, char32_t character, bool wasDown) = 0; + virtual void OnKey(int key, + int scancode, + int action, + char32_t character, + bool wasDown) = 0; // Notifies delegate that backing window size has recevied scroll. // Typically called by currently configured WindowBindingHandler From cf653a2696b03865cf4d24dbc46c63cdea2c2052 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 16:59:17 -0800 Subject: [PATCH 07/82] Fix build gn --- shell/platform/windows/BUILD.gn | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 507c1ba981ceb..43b388a776c86 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -48,9 +48,9 @@ source_set("flutter_windows_source") { "angle_surface_manager.h", "cursor_handler.cc", "cursor_handler.h", + "flutter_key_map.cc", "flutter_keyboard_manager.cc", "flutter_keyboard_manager.h", - "flutter_key_map.cc", "flutter_project_bundle.cc", "flutter_project_bundle.h", "flutter_windows.cc", @@ -161,8 +161,8 @@ if (target_os == "winuwp") { testonly = true sources = [ - "flutter_project_bundle_unittests.cc", "flutter_keyboard_manager_unittests.cc", + "flutter_project_bundle_unittests.cc", "flutter_windows_engine_unittests.cc", "string_conversion_unittests.cc", "system_utils_unittests.cc", From 08c95070cfcdfdbaed501d13880b2ef71879a95c Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 17:32:36 -0800 Subject: [PATCH 08/82] Include embedder changes --- ci/licenses_golden/licenses_flutter | 2 + lib/ui/dart_ui.gni | 1 + lib/ui/hooks.dart | 6 ++ lib/ui/key.dart | 68 +++++++++++++++++++ lib/ui/platform_dispatcher.dart | 61 ++++++++++++++++- lib/ui/ui.dart | 1 + lib/ui/window.dart | 9 +++ lib/ui/window/key_data.cc | 18 +++++ lib/ui/window/key_data.h | 48 +++++++++++++ lib/ui/window/key_data_packet.cc | 36 ++++++++++ lib/ui/window/key_data_packet.h | 65 ++++++++++++++++++ lib/ui/window/window.cc | 16 +++++ lib/ui/window/window.h | 2 + runtime/runtime_controller.cc | 10 +++ runtime/runtime_controller.h | 8 +++ shell/common/engine.cc | 7 ++ shell/common/engine.h | 11 +++ shell/common/platform_view.cc | 5 ++ shell/common/platform_view.h | 24 +++++++ shell/common/shell.cc | 15 ++++ shell/common/shell.h | 4 ++ shell/common/shell_unittests.cc | 3 + shell/platform/embedder/embedder.cc | 61 +++++++++++++++++ shell/platform/embedder/embedder.h | 40 +++++++++++ shell/platform/embedder/embedder_engine.cc | 15 ++++ shell/platform/embedder/embedder_engine.h | 2 + .../fuchsia/flutter/platform_view_unittest.cc | 3 + 27 files changed, 540 insertions(+), 1 deletion(-) create mode 100644 lib/ui/key.dart create mode 100644 lib/ui/window/key_data.cc create mode 100644 lib/ui/window/key_data.h create mode 100644 lib/ui/window/key_data_packet.cc create mode 100644 lib/ui/window/key_data_packet.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 12543e387ecfc..e868c7ca49eb8 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1091,6 +1091,7 @@ FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.cc FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.h FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.cc FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.h +FILE: ../../../flutter/lib/ui/key.dart FILE: ../../../flutter/lib/ui/lerp.dart FILE: ../../../flutter/lib/ui/natives.dart FILE: ../../../flutter/lib/ui/painting.dart @@ -1330,6 +1331,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/ui/compositing.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/geometry.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/hash_codes.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/initialization.dart +FILE: ../../../flutter/lib/web_ui/lib/src/ui/key.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/lerp.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/natives.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/painting.dart diff --git a/lib/ui/dart_ui.gni b/lib/ui/dart_ui.gni index 11fa74da53f84..96a7b06fbd918 100644 --- a/lib/ui/dart_ui.gni +++ b/lib/ui/dart_ui.gni @@ -10,6 +10,7 @@ dart_ui_files = [ "//flutter/lib/ui/hash_codes.dart", "//flutter/lib/ui/hooks.dart", "//flutter/lib/ui/isolate_name_server.dart", + "//flutter/lib/ui/key.dart", "//flutter/lib/ui/lerp.dart", "//flutter/lib/ui/natives.dart", "//flutter/lib/ui/painting.dart", diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index 6ffcea0f93d72..8d6770471ad71 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -96,6 +96,12 @@ void _dispatchPointerDataPacket(ByteData packet) { PlatformDispatcher.instance._dispatchPointerDataPacket(packet); } +@pragma('vm:entry-point') +// ignore: unused_element +void _dispatchKeyData(ByteData packet) { + PlatformDispatcher.instance._dispatchKeyData(packet); +} + @pragma('vm:entry-point') // ignore: unused_element void _dispatchSemanticsAction(int id, int action, ByteData? args) { diff --git a/lib/ui/key.dart b/lib/ui/key.dart new file mode 100644 index 0000000000000..e60bfbeb027f1 --- /dev/null +++ b/lib/ui/key.dart @@ -0,0 +1,68 @@ +// 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. + +// @dart = 2.12 + +part of dart.ui; + +/// How the key has changed since the last report. +/// +// Must match the KeyChange enum in ui/window/key_data.h. +enum KeyChange { + /// The key is pressed. + down, + + /// The key is released. + up, + + /// The key is held, causing a repeated key input. + repeat, +} + +/// Information about the change of a key. +class KeyData { + /// Creates an object that represents the change of a key. + const KeyData({ + required this.timeStamp, + required this.change, + required this.physical, + required this.logical, + required this.character, + required this.synthesized, + }); + + /// Time of event dispatch, relative to an arbitrary timeline. + /// + /// For [KeyChange.synchronize] and [KeyChange.cancel] events, the [timeStamp] + /// might not be the actual time that the key press or release happens. + final Duration timeStamp; + + /// How the key has changed since the last report. + final KeyChange change; + + /// The key code for the physical key that has changed. + final int physical; + + /// The key code for the logical key that has changed. + final int logical; + + /// Character input from the event. + /// + /// Not available to up events. + final String? character; + + /// Whether the event is generated by Flutter, hence not corresponding to a + /// native event. + final bool synthesized; + + @override + String toString() => 'KeyData(timeStamp: $timeStamp, change: $change, physical: $physical, ' + 'logical: $logical, character: $character, synthesized: $synthesized)'; + + /// Returns a complete textual description of the information in this object. + String toStringFull() { + return '$runtimeType(' + ')'; + } +} diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index dc7223b79cc5d..c93fe976cc518 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -28,6 +28,9 @@ typedef TimingsCallback = void Function(List timings); /// Signature for [PlatformDispatcher.onPointerDataPacket]. typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); +/// Signature for [PlatformDispatcher.onKeyData]. +typedef KeyDataCallback = void Function(KeyData data); + /// Signature for [PlatformDispatcher.onSemanticsAction]. typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); @@ -332,6 +335,62 @@ class PlatformDispatcher { return PointerDataPacket(data: data); } + /// A callback that is invoked when key data is available. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + KeyDataCallback? get onKeyData => _onKeyData; + KeyDataCallback? _onKeyData; + Zone _onKeyDataZone = Zone.root; + set onKeyData(KeyDataCallback? callback) { + _onKeyData = callback; + _onKeyDataZone = Zone.current; + } + + // Called from the engine, via hooks.dart + void _dispatchKeyData(ByteData packet) { + print('dispatch: onKeyData $onKeyData'); + if (onKeyData != null) { + _invoke1( + onKeyData, + _onKeyDataZone, + _unpackKeyData(packet), + ); + } + } + + // If this value changes, update the encoding code in the following files: + // + // * key_data.h + // * key.dart (ui) + // * key.dart (web_ui) + // * HardwareKeyboard.java + static const int _kKeyDataFieldCount = 5; + + // KeyData packet structure: + // | CharDataSize | (1 field) + // | Key Data | (_kKeyDataFieldCount fields) + // | CharData | (CharDataSize bits) + static KeyData _unpackKeyData(ByteData packet) { + const int kStride = Int64List.bytesPerElement; + + int offset = 0; + final int charDataSize = packet.getUint64(kStride * offset++, _kFakeHostEndian); + final String? character = charDataSize == 0 ? null : utf8.decoder.convert( + packet.buffer.asUint8List(kStride * (offset + _kKeyDataFieldCount), charDataSize)); + + final KeyData keyData = KeyData( + timeStamp: Duration(microseconds: packet.getUint64(kStride * offset++, _kFakeHostEndian)), + change: KeyChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], + physical: packet.getUint64(kStride * offset++, _kFakeHostEndian), + logical: packet.getUint64(kStride * offset++, _kFakeHostEndian), + character: character, + synthesized: packet.getUint64(kStride * offset++, _kFakeHostEndian) != 0, + ); + + return keyData; + } + /// A callback that is invoked to report the [FrameTiming] of recently /// rasterized frames. /// @@ -1547,4 +1606,4 @@ class Locale { out.write('$separator$countryCode'); return out.toString(); } -} \ No newline at end of file +} diff --git a/lib/ui/ui.dart b/lib/ui/ui.dart index ea966c88cd43a..f48b8162ba42c 100644 --- a/lib/ui/ui.dart +++ b/lib/ui/ui.dart @@ -30,6 +30,7 @@ part 'geometry.dart'; part 'hash_codes.dart'; part 'hooks.dart'; part 'isolate_name_server.dart'; +part 'key.dart'; part 'lerp.dart'; part 'natives.dart'; part 'painting.dart'; diff --git a/lib/ui/window.dart b/lib/ui/window.dart index ac984a2a6581c..da65c1d625a5a 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -556,6 +556,15 @@ class SingletonFlutterWindow extends FlutterWindow { platformDispatcher.onPointerDataPacket = callback; } + /// A callback that is invoked when key data is available. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + KeyDataCallback? get onKeyData => platformDispatcher.onKeyData; + set onKeyData(KeyDataCallback? callback) { + platformDispatcher.onKeyData = callback; + } + /// The route or path that the embedder requested when the application was /// launched. /// diff --git a/lib/ui/window/key_data.cc b/lib/ui/window/key_data.cc new file mode 100644 index 0000000000000..875f89f7522c2 --- /dev/null +++ b/lib/ui/window/key_data.cc @@ -0,0 +1,18 @@ +// 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/lib/ui/window/key_data.h" + +#include + +namespace flutter { + +static_assert(sizeof(KeyData) == kBytesPerKeyField * kKeyDataFieldCount, + "KeyData has the wrong size"); + +void KeyData::Clear() { + memset(this, 0, sizeof(KeyData)); +} + +} // namespace flutter diff --git a/lib/ui/window/key_data.h b/lib/ui/window/key_data.h new file mode 100644 index 0000000000000..d41c162f47f8a --- /dev/null +++ b/lib/ui/window/key_data.h @@ -0,0 +1,48 @@ +// 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_LIB_UI_WINDOW_KEY_DATA_H_ +#define FLUTTER_LIB_UI_WINDOW_KEY_DATA_H_ + +#include + +namespace flutter { + +// If this value changes, update the key data unpacking code in hooks.dart. +static constexpr int kKeyDataFieldCount = 5; +static constexpr int kBytesPerKeyField = sizeof(int64_t); + +// The change of the key event, used by KeyData. +// +// Must match the KeyChange enum in ui/key.dart. +enum class KeyChange : int64_t { + kDown = 0, + kUp, + kRepeat, +}; + +// The fixed-length sections of a KeyDataPacket. +// +// KeyData does not contain `character`, for various-length data are stored in a +// different way in KeyDataPacket. +// +// This structure is unpacked by hooks.dart. +struct alignas(8) KeyData { + // Timestamp in microseconds from an arbitrary and consistant start point + uint64_t timestamp; + KeyChange change; + uint64_t physical; + uint64_t logical; + // True if the event does not correspond to a native event. + // + // The value is 1 for true, and 0 for false. + uint64_t synthesized; + + // Set all contents of `Keydata` to 0. + void Clear(); +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_H_ diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc new file mode 100644 index 0000000000000..0a22b04f13d9d --- /dev/null +++ b/lib/ui/window/key_data_packet.cc @@ -0,0 +1,36 @@ +// 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/lib/ui/window/key_data_packet.h" +#include "flutter/fml/logging.h" + +namespace flutter { + +KeyDataPacket::KeyDataPacket(uint8_t* data, size_t num_bytes) + : data_(data, data + num_bytes) {} + +KeyDataPacket::KeyDataPacket(size_t num_bytes) : data_(num_bytes) {} + +KeyDataPacket::~KeyDataPacket() = default; + +KeyDataPacketBuilder::KeyDataPacketBuilder(size_t character_data_size) + : KeyDataPacket(sizeof(uint64_t) + sizeof(KeyData) + character_data_size) { + uint64_t size64 = character_data_size; + memcpy(&data()[CharacterSizeStart_()], &size64, sizeof(size64)); +} + +KeyDataPacketBuilder::~KeyDataPacketBuilder() = default; + +void KeyDataPacketBuilder::SetKeyData(const KeyData& event) { + memcpy(&data()[KeyDataStart_()], &event, sizeof(KeyData)); +} + +void KeyDataPacketBuilder::SetCharacter(const char* character) { + if (character != nullptr) { + memcpy(data().data() + CharacterStart_(), character, + data().size() - CharacterStart_()); + } +} + +} // namespace flutter diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h new file mode 100644 index 0000000000000..fa48a60f7b676 --- /dev/null +++ b/lib/ui/window/key_data_packet.h @@ -0,0 +1,65 @@ +// 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_LIB_UI_WINDOW_KEY_DATA_PACKET_H_ +#define FLUTTER_LIB_UI_WINDOW_KEY_DATA_PACKET_H_ + +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/lib/ui/window/key_data.h" + +namespace flutter { + +// Bitstream that contains a KeyData. +// +// This class provides interface to read the opaque bits. For constructing such +// a bitstream from KeyData, checkout KeyDataPacketBuilder. +class KeyDataPacket { + public: + KeyDataPacket(uint8_t* data, size_t num_bytes); + + protected: + KeyDataPacket(size_t num_bytes); + + std::vector& data() { return data_; } + + public: + ~KeyDataPacket(); + + const std::vector& data() const { return data_; } + + private: + std::vector data_; + + FML_DISALLOW_COPY_AND_ASSIGN(KeyDataPacket); +}; + +// Build a KeyDataPacket bitstream gradually. +class KeyDataPacketBuilder : public KeyDataPacket { + public: + // Build a KeyDataPacket by incrementally fill in data. + // + // The `character_data_size` is number of bytes to contain the character data. + KeyDataPacketBuilder(size_t character_data_size); + ~KeyDataPacketBuilder(); + + void SetKeyData(const KeyData& event); + + // Set character data to the proper position, which should not be terminated + // by a null character (length controled by character_data_size). + void SetCharacter(const char* characters); + + private: + size_t CharacterSizeStart_() { return 0; } + size_t KeyDataStart_() { return CharacterSizeStart_() + sizeof(uint64_t); } + size_t CharacterStart_() { return KeyDataStart_() + sizeof(KeyData); } + + FML_DISALLOW_COPY_AND_ASSIGN(KeyDataPacketBuilder); +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_PACKET_H_ diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 082df1b823d32..a6cbf7f35a40c 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -36,6 +36,22 @@ void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) { library_.value(), "_dispatchPointerDataPacket", {data_handle})); } +void Window::DispatchKeyDataPacket(const KeyDataPacket& packet) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + + const std::vector& buffer = packet.data(); + Dart_Handle data_handle = + tonic::DartByteData::Create(buffer.data(), buffer.size()); + if (Dart_IsError(data_handle)) { + return; + } + tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_dispatchKeyData", + {data_handle})); +} + void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { viewport_metrics_ = metrics; diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index b6fa2555b04d7..d81e7e95aaa86 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -9,6 +9,7 @@ #include #include +#include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/viewport_metrics.h" @@ -27,6 +28,7 @@ class Window final { const ViewportMetrics& viewport_metrics() const { return viewport_metrics_; } void DispatchPointerDataPacket(const PointerDataPacket& packet); + void DispatchKeyDataPacket(const KeyDataPacket& packet); void UpdateWindowMetrics(const ViewportMetrics& metrics); private: diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 4998a9aeb3f50..b8a0327a393a9 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -243,6 +243,16 @@ bool RuntimeController::DispatchPointerDataPacket( return false; } +bool RuntimeController::DispatchKeyDataPacket(const KeyDataPacket& packet) { + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataPacket", "mode", + "basic"); + platform_configuration->get_window(0)->DispatchKeyDataPacket(packet); + return true; + } + return false; +} + bool RuntimeController::DispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) { diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 5e2c26da52726..6f42f440c4b38 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -408,6 +408,14 @@ class RuntimeController : public PlatformConfigurationClient { /// bool DispatchPointerDataPacket(const PointerDataPacket& packet); + //---------------------------------------------------------------------------- + /// @brief Dispatch the specified pointer data message to the running + /// root isolate. + /// + /// @param[in] packet The key data message to dispatch to the isolate. + /// + bool DispatchKeyDataPacket(const KeyDataPacket& packet); + //---------------------------------------------------------------------------- /// @brief Dispatch the semantics action to the specified accessibility /// node. diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 84de3e25bf92c..6b49eab9badcd 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -384,6 +384,13 @@ void Engine::DispatchPointerDataPacket( pointer_data_dispatcher_->DispatchPacket(std::move(packet), trace_flow_id); } +void Engine::DispatchKeyDataPacket(std::unique_ptr packet) { + TRACE_EVENT0("flutter", "Engine::DispatchKeyDataPacket"); + if (runtime_controller_) { + runtime_controller_->DispatchKeyDataPacket(*packet); + } +} + void Engine::DispatchSemanticsAction(int id, SemanticsAction action, std::vector args) { diff --git a/shell/common/engine.h b/shell/common/engine.h index 8041e14199add..cf2234ac6c07d 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -710,6 +710,17 @@ class Engine final : public RuntimeDelegate, void DispatchPointerDataPacket(std::unique_ptr packet, uint64_t trace_flow_id); + //---------------------------------------------------------------------------- + /// @brief Notifies the engine that the embedder has sent it a key data + /// packet. A key data packet contains one physical key event and + /// one or multiple logical key events. This call originates in + /// the platform view and the shell has forwarded the same to the + /// engine on the UI task runner here. + /// + /// @param[in] packet The key data packet. + /// + void DispatchKeyDataPacket(std::unique_ptr packet); + //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder encountered an /// accessibility related action on the specified node. This call diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 22c2b395ba76e..168087be13a35 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -42,6 +42,11 @@ void PlatformView::DispatchPointerDataPacket( pointer_data_packet_converter_.Convert(std::move(packet))); } +void PlatformView::DispatchKeyDataPacket( + std::unique_ptr packet) { + delegate_.OnPlatformViewDispatchKeyDataPacket(std::move(packet)); +} + void PlatformView::DispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) { diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 92e670677c25a..d8beccd37d6b1 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -16,6 +16,7 @@ #include "flutter/fml/memory/weak_ptr.h" #include "flutter/lib/ui/semantics/custom_accessibility_action.h" #include "flutter/lib/ui/semantics/semantics_node.h" +#include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/pointer_data_packet_converter.h" @@ -125,6 +126,19 @@ class PlatformView { virtual void OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) = 0; + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate that the platform view has encountered + /// a key event. This key event needs to be forwarded to + /// the running root isolate hosted by the engine on the UI + /// thread. + /// + /// @param[in] packet The key data packet containing one physical key + /// event + /// and multiple logical key events. + /// + virtual void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet) = 0; + //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view has encountered /// an accessibility related action on the specified node. This @@ -575,6 +589,16 @@ class PlatformView { /// void DispatchPointerDataPacket(std::unique_ptr packet); + //---------------------------------------------------------------------------- + /// @brief Dispatches key events from the embedder to the framework. Each + /// key data packet contains one physical event and multiple + /// logical key events. Each call to this method wakes up the UI + /// thread. + /// + /// @param[in] packet The key data packet to dispatch to the framework. + /// + void DispatchKeyDataPacket(std::unique_ptr packet); + //-------------------------------------------------------------------------- /// @brief Used by the embedder to specify a texture that it wants the /// rasterizer to composite within the Flutter layer tree. All diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 98d8f43f2813c..db0938ed8ce0b 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -875,6 +875,21 @@ void Shell::OnPlatformViewDispatchPointerDataPacket( next_pointer_flow_id_++; } +// |PlatformView::Delegate| +void Shell::OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet) { + TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataPacket"); + FML_DCHECK(is_setup_); + FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( + [engine = weak_engine_, packet = std::move(packet)]() mutable { + if (engine) { + engine->DispatchKeyDataPacket(std::move(packet)); + } + })); +} + // |PlatformView::Delegate| void Shell::OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, diff --git a/shell/common/shell.h b/shell/common/shell.h index 21214f0013f34..480d8077daaa6 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -456,6 +456,10 @@ class Shell final : public PlatformView::Delegate, void OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) override; + // |PlatformView::Delegate| + void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet) override; + // |PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction( int32_t id, diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 98621f2f0f75e..d32aad267d912 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -62,6 +62,9 @@ class MockPlatformViewDelegate : public PlatformView::Delegate { MOCK_METHOD1(OnPlatformViewDispatchPointerDataPacket, void(std::unique_ptr packet)); + MOCK_METHOD1(OnPlatformViewDispatchKeyDataPacket, + void(std::unique_ptr packet)); + MOCK_METHOD3(OnPlatformViewDispatchSemanticsAction, void(int32_t id, SemanticsAction action, diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index b2b9f26a53909..f580eb8e67fc8 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1492,6 +1492,66 @@ FlutterEngineResult FlutterEngineSendPointerEvent( "running Flutter application."); } +inline flutter::KeyChange ToKeyChange(FlutterKeyEventKind key_change) { + switch (key_change) { + case kFlutterKeyEventKindUp: + return flutter::KeyChange::kUp; + case kFlutterKeyEventKindDown: + return flutter::KeyChange::kDown; + case kFlutterKeyEventKindRepeat: + return flutter::KeyChange::kRepeat; + } + return flutter::KeyChange::kUp; +} + +// The number of bytes that should be able to fully store character data. +// +// This is an arbitrary number that is considered sufficient, used as an +// upperbound in strnlen. +// +// Many platforms assert the character to be less than 2 int16's, i.e. 4 bytes, +// therefore the character data is asserted to be less than double the amount, +// i.e. 8 bytes. +constexpr size_t kKeyEventCharacterMaxBytes = 8; + +FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) + engine, + const FlutterKeyEvent* event) { + if (engine == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); + } + + if (event == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid key event."); + } + + const char* character = SAFE_ACCESS(event, character, nullptr); + + size_t character_data_size = + character == nullptr ? 0 : strnlen(character, kKeyEventCharacterMaxBytes); + + auto packet = + std::make_unique(character_data_size); + + flutter::KeyData key_data; + key_data.Clear(); + key_data.timestamp = (uint64_t)SAFE_ACCESS(event, timestamp, 0); + key_data.change = ToKeyChange( + SAFE_ACCESS(event, kind, FlutterKeyEventKind::kFlutterKeyEventKindUp)); + key_data.physical = SAFE_ACCESS(event, physical, 0); + key_data.logical = SAFE_ACCESS(event, logical, 0); + key_data.synthesized = !!SAFE_ACCESS(event, synthesized, false); + packet->SetKeyData(key_data); + packet->SetCharacter(character); + + return reinterpret_cast(engine) + ->DispatchKeyDataPacket(std::move(packet)) + ? kSuccess + : LOG_EMBEDDER_ERROR(kInternalInconsistency, + "Could not dispatch the key event to the " + "running Flutter application."); +} + FlutterEngineResult FlutterEngineSendPlatformMessage( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage* flutter_message) { @@ -2166,6 +2226,7 @@ FlutterEngineResult FlutterEngineGetProcAddresses( SET_PROC(RunInitialized, FlutterEngineRunInitialized); SET_PROC(SendWindowMetricsEvent, FlutterEngineSendWindowMetricsEvent); SET_PROC(SendPointerEvent, FlutterEngineSendPointerEvent); + SET_PROC(SendKeyEvent, FlutterEngineSendKeyEvent); SET_PROC(SendPlatformMessage, FlutterEngineSendPlatformMessage); SET_PROC(PlatformMessageCreateResponseHandle, FlutterPlatformMessageCreateResponseHandle); diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 71228696308cd..9f02e3cabebf3 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -611,6 +611,36 @@ typedef struct { int64_t buttons; } FlutterPointerEvent; +typedef enum { + kFlutterKeyEventKindUp = 1, + kFlutterKeyEventKindDown, + kFlutterKeyEventKindRepeat, +} FlutterKeyEventKind; + +typedef struct { + /// The size of this struct. Must be sizeof(FlutterKeyEvent). + size_t struct_size; + // Timestamp in microseconds. + double timestamp; + // The event kind. + FlutterKeyEventKind kind; + // The physical key of the event, distinguished by the HID code. + uint64_t physical; + // The logical key of the event, usually the effect of the key without + // modifier, but might include modifier if the information is not available. + // Can be 0 for empty. + uint64_t logical; + // Null-terminated character input from the event. Can be null. Not available + // to up events. + const char* character; + // Whether the event is synthesized by Flutter. + bool synthesized; +} FlutterKeyEvent; + +struct _FlutterPlatformKeyEventResponseHandle; +typedef struct _FlutterPlatformKeyEventResponseHandle + FlutterPlatformKeyEventResponseHandle; + struct _FlutterPlatformMessageResponseHandle; typedef struct _FlutterPlatformMessageResponseHandle FlutterPlatformMessageResponseHandle; @@ -1549,6 +1579,12 @@ FlutterEngineResult FlutterEngineSendPointerEvent( const FlutterPointerEvent* events, size_t events_count); +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineSendKeyEvent( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterKeyEvent* event, + FlutterPlatformKeyEventResponseHandle** response_out); + FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendPlatformMessage( FLUTTER_API_SYMBOL(FlutterEngine) engine, @@ -2062,6 +2098,9 @@ typedef FlutterEngineResult (*FlutterEngineSendPointerEventFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent* events, size_t events_count); +typedef FlutterEngineResult (*FlutterEngineSendKeyEventFnPtr)( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterKeyEvent* event); typedef FlutterEngineResult (*FlutterEngineSendPlatformMessageFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage* message); @@ -2155,6 +2194,7 @@ typedef struct { FlutterEngineRunInitializedFnPtr RunInitialized; FlutterEngineSendWindowMetricsEventFnPtr SendWindowMetricsEvent; FlutterEngineSendPointerEventFnPtr SendPointerEvent; + FlutterEngineSendKeyEventFnPtr SendKeyEvent; FlutterEngineSendPlatformMessageFnPtr SendPlatformMessage; FlutterEnginePlatformMessageCreateResponseHandleFnPtr PlatformMessageCreateResponseHandle; diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index bb0aa08bb42d5..32685c7b00ca0 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -136,6 +136,21 @@ bool EmbedderEngine::DispatchPointerDataPacket( return true; } +bool EmbedderEngine::DispatchKeyDataPacket( + std::unique_ptr packet) { + if (!IsValid() || !packet) { + return false; + } + + auto platform_view = shell_->GetPlatformView(); + if (!platform_view) { + return false; + } + + platform_view->DispatchKeyDataPacket(std::move(packet)); + return true; +} + bool EmbedderEngine::SendPlatformMessage( fml::RefPtr message) { if (!IsValid() || !message) { diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index 4f38ba4410cd0..53a7b315c219a 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -60,6 +60,8 @@ class EmbedderEngine { bool DispatchPointerDataPacket( std::unique_ptr packet); + bool DispatchKeyDataPacket(std::unique_ptr packet); + bool SendPlatformMessage(fml::RefPtr message); bool RegisterTexture(int64_t texture); diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 7fabcbd11d1ba..76239b254a566 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -81,6 +81,9 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { void OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) {} // |flutter::PlatformView::Delegate| + void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet) {} + // |flutter::PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction(int32_t id, flutter::SemanticsAction action, std::vector args) {} From aac6837fb42bc47eb40c38c24326cc373cbfa03c Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 17:55:43 -0800 Subject: [PATCH 09/82] Fix buildgn for embedder --- lib/ui/BUILD.gn | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index fa31f9fe5575e..18521e8818a97 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -93,6 +93,10 @@ source_set("ui") { "ui_dart_state.h", "volatile_path_tracker.cc", "volatile_path_tracker.h", + "window/key_data.cc", + "window/key_data.h", + "window/key_data_packet.cc", + "window/key_data_packet.h", "window/platform_configuration.cc", "window/platform_configuration.h", "window/platform_message.cc", From a2d0c78b35c9d5f860f53875db07bc59d3fe8ee7 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 18:06:37 -0800 Subject: [PATCH 10/82] Fix license --- lib/web_ui/lib/src/engine/key_map.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/key_map.dart b/lib/web_ui/lib/src/engine/key_map.dart index d3970549c7ca2..03ef664c1dec0 100644 --- a/lib/web_ui/lib/src/engine/key_map.dart +++ b/lib/web_ui/lib/src/engine/key_map.dart @@ -1,4 +1,4 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. +// 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. From 226cd56ea5790ebf2f6f79804eaa5439c1fb1b42 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 20:16:00 -0800 Subject: [PATCH 11/82] Fix license --- ci/licenses_golden/licenses_flutter | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index e868c7ca49eb8..ab9a23bfc46fe 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1179,6 +1179,10 @@ FILE: ../../../flutter/lib/ui/ui_dart_state.h FILE: ../../../flutter/lib/ui/volatile_path_tracker.cc FILE: ../../../flutter/lib/ui/volatile_path_tracker.h FILE: ../../../flutter/lib/ui/window.dart +FILE: ../../../flutter/lib/ui/window/key_data.cc +FILE: ../../../flutter/lib/ui/window/key_data.h +FILE: ../../../flutter/lib/ui/window/key_data_packet.cc +FILE: ../../../flutter/lib/ui/window/key_data_packet.h FILE: ../../../flutter/lib/ui/window/platform_configuration.cc FILE: ../../../flutter/lib/ui/window/platform_configuration.h FILE: ../../../flutter/lib/ui/window/platform_configuration_unittests.cc @@ -1271,7 +1275,9 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface_stats.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/transform.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html_image_codec.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/key_map.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/keyboard.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/keyboard_binding.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/mouse_cursor.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/navigation/history.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/navigation/js_url_strategy.dart From 38280559ca4a606b56924de624edfce27ef1a445 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 23:46:00 -0800 Subject: [PATCH 12/82] Fix VoidCallback --- lib/web_ui/lib/src/engine/keyboard_binding.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index c0f734b10bebd..13630a65837cd 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -5,7 +5,7 @@ // @dart = 2.12 part of engine; -typedef VoidCallback = void Function(); +typedef _VoidCallback = void Function(); typedef ValueGetter = T Function(); typedef _ModifierGetter = bool Function(FlutterHtmlKeyboardEvent event); @@ -149,7 +149,7 @@ class AsyncKeyboardDispatching { }); final ui.KeyData keyData; - final VoidCallback? callback; + final _VoidCallback? callback; } // A wrapper of [html.KeyboardEvent] with reduced methods delegated to the event @@ -275,7 +275,7 @@ class KeyboardConverter { // // Returns a callback that cancels the schedule. Disposal of // `KeyBoardConverter` also cancels the shedule automatically. - VoidCallback _scheduleAsyncEvent(Duration duration, ValueGetter getData, VoidCallback callback) { + _VoidCallback _scheduleAsyncEvent(Duration duration, ValueGetter getData, _VoidCallback callback) { bool canceled = false; Future.delayed(duration).then((_) { if (!canceled && !_disposed) { @@ -296,10 +296,10 @@ class KeyboardConverter { // while the key is held down by the user. If we don't receive a repeat // event within a specific duration ([_keydownCancelDuration]) we assume // the user has released the key and we synthesize a keyup event. - final Map _keyGuards = {}; + final Map _keyGuards = {}; // Call this method on the down or repeated event of a non-modifier key. void _startGuardingKey(int physicalKey, int logicalKey, Duration currentTimeStamp) { - final VoidCallback cancelingCallback = _scheduleAsyncEvent( + final _VoidCallback cancelingCallback = _scheduleAsyncEvent( _keydownCancelDuration, () => ui.KeyData( timeStamp: currentTimeStamp + _keydownCancelDuration, From 99cd26c70aeb30da68bbf70dcbe2e48b8a575282 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 7 Jan 2021 15:30:29 -0800 Subject: [PATCH 13/82] Fix tests --- lib/web_ui/lib/src/engine/keyboard_binding.dart | 10 +++++----- lib/web_ui/test/keyboard_converter_test.dart | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index 13630a65837cd..4107eed9978e0 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -41,10 +41,11 @@ final Map _kLogicalKeyToModifierGetter = { const Duration _kKeydownCancelDurationNormal = Duration(milliseconds: 1000); const Duration _kKeydownCancelDurationMacOs = Duration(milliseconds: 2000); -late final int _kCharLowerA = 'a'.codeUnitAt(0); -late final int _kCharLowerZ = 'z'.codeUnitAt(0); -late final int _kCharUpperA = 'A'.codeUnitAt(0); -late final int _kCharUpperZ = 'Z'.codeUnitAt(0); +// ASCII for a, z, A, and Z +const int _kCharLowerA = 0x61; +const int _kCharLowerZ = 0x7a; +const int _kCharUpperA = 0x41; +const int _kCharUpperZ = 0x5a; bool isAlphabet(int charCode) { return (charCode >= _kCharLowerA && charCode <= _kCharLowerZ) || (charCode >= _kCharUpperA && charCode <= _kCharUpperZ); @@ -445,7 +446,6 @@ class KeyboardConverter { // events can not be deduced since we don't know which physical key they // represent. _kLogicalKeyToModifierGetter.forEach((int logicalKey, _ModifierGetter getModifier) { - // print(_pressingRecords); if (_pressingRecords.containsValue(logicalKey) && !getModifier(event)) { _pressingRecords.removeWhere((int physicalKey, int logicalRecord) { if (logicalRecord != logicalKey) diff --git a/lib/web_ui/test/keyboard_converter_test.dart b/lib/web_ui/test/keyboard_converter_test.dart index eb5bcf5563dfd..3b0003a1edbc8 100644 --- a/lib/web_ui/test/keyboard_converter_test.dart +++ b/lib/web_ui/test/keyboard_converter_test.dart @@ -27,8 +27,8 @@ const int kPhysicalTab = 0x0007002b; const int kPhysicalCapsLock = 0x00070039; const int kPhysicalScrollLock = 0x00070047; -const int kLogicalKeyA = 0x00000000041; -const int kLogicalKeyU = 0x00000000055; +const int kLogicalKeyA = 0x00000000061; +const int kLogicalKeyU = 0x00000000075; const int kLogicalDigit1 = 0x00000000031; const int kLogicalNumpad1 = 0x00200000031; const int kLogicalShiftLeft = 0x030000010d; From 09ccc39b36d97927ad5842cd7c6474f07e247f27 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 7 Jan 2021 17:05:16 -0800 Subject: [PATCH 14/82] Fix compile --- testing/dart/window_hooks_integration_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/testing/dart/window_hooks_integration_test.dart b/testing/dart/window_hooks_integration_test.dart index 91c2036dbda91..3a1aac710f9f9 100644 --- a/testing/dart/window_hooks_integration_test.dart +++ b/testing/dart/window_hooks_integration_test.dart @@ -24,6 +24,7 @@ part '../../lib/ui/compositing.dart'; part '../../lib/ui/geometry.dart'; part '../../lib/ui/hash_codes.dart'; part '../../lib/ui/hooks.dart'; +part '../../lib/ui/key.dart'; part '../../lib/ui/lerp.dart'; part '../../lib/ui/natives.dart'; part '../../lib/ui/painting.dart'; From c10ca64fe968511b89ac4127c6e95c20815f696f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 7 Jan 2021 17:56:32 -0800 Subject: [PATCH 15/82] Fix compile: implement virtual methods --- .../darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm | 1 + .../darwin/ios/framework/Source/FlutterPlatformViewsTest.mm | 1 + .../darwin/ios/framework/Source/accessibility_bridge_test.mm | 1 + 3 files changed, 3 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm index c81910f892767..129cce47c8199 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm @@ -25,6 +25,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 8f4b3e7a972c4..631f98c30c887 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -95,6 +95,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index 08f01b7c705ed..a446224495925 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -78,6 +78,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} From 7a3dd539101c2834eefe77c2d2f1b944cd711d85 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 8 Jan 2021 02:16:36 -0800 Subject: [PATCH 16/82] Better toString --- lib/ui/key.dart | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/ui/key.dart b/lib/ui/key.dart index e60bfbeb027f1..35cfd76501153 100644 --- a/lib/ui/key.dart +++ b/lib/ui/key.dart @@ -57,12 +57,29 @@ class KeyData { final bool synthesized; @override - String toString() => 'KeyData(timeStamp: $timeStamp, change: $change, physical: $physical, ' - 'logical: $logical, character: $character, synthesized: $synthesized)'; + String toString() => 'KeyData(change: ${_changeToString(change)}, physical: 0x${physical.toRadixString(16)}, ' + 'logical: 0x${logical.toRadixString(16)}, character: $character)'; /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(' + 'change: ${_changeToString(change)}, ' + 'timeStamp: $timeStamp, ' + 'physical: 0x${physical.toRadixString(16)}, ' + 'logical: 0x${logical.toRadixString(16)}, ' + 'character: $character, ' + 'synthesized: $synthesized' ')'; } + + static String _changeToString(KeyChange change) { + switch (change) { + case KeyChange.up: + return 'up'; + case KeyChange.down: + return 'down'; + case KeyChange.repeat: + return 'repeat'; + } + } } From 736d4f6f1cda02e23cab4d35cb360611c2a6ccc2 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 19 Jan 2021 14:45:13 -0800 Subject: [PATCH 17/82] Fix doc. Add async callback --- ci/licenses_golden/licenses_flutter | 4 +- lib/ui/BUILD.gn | 4 +- lib/ui/hooks.dart | 4 +- lib/ui/key.dart | 35 +++++++-- lib/ui/platform_dispatcher.dart | 28 ++++--- lib/ui/window/key_data.h | 6 +- lib/ui/window/key_data_message.cc | 29 +++++++ .../{key_data_packet.h => key_data_message.h} | 48 ++++-------- lib/ui/window/key_data_packet.cc | 36 --------- lib/ui/window/platform_configuration.cc | 32 ++++++++ lib/ui/window/platform_configuration.h | 26 +++++++ lib/ui/window/window.cc | 6 +- lib/ui/window/window.h | 5 +- .../lib/src/engine/keyboard_binding.dart | 4 +- .../lib/src/engine/platform_dispatcher.dart | 9 ++- lib/web_ui/lib/src/ui/key.dart | 52 +++++++++++-- .../lib/src/ui/platform_dispatcher.dart | 2 +- runtime/runtime_controller.cc | 9 ++- runtime/runtime_controller.h | 3 +- shell/common/engine.cc | 8 +- shell/common/engine.h | 3 +- shell/common/platform_view.cc | 7 +- shell/common/platform_view.h | 10 ++- shell/common/shell.cc | 13 ++-- shell/common/shell.h | 5 +- shell/common/shell_unittests.cc | 5 +- .../Source/FlutterEnginePlatformViewTest.mm | 2 +- .../Source/FlutterPlatformViewsTest.mm | 2 +- .../Source/accessibility_bridge_test.mm | 2 +- shell/platform/embedder/embedder.cc | 25 +++--- shell/platform/embedder/embedder.h | 78 +++++++++++++++---- shell/platform/embedder/embedder_engine.cc | 9 ++- shell/platform/embedder/embedder_engine.h | 3 +- .../fuchsia/flutter/platform_view_unittest.cc | 4 +- 34 files changed, 352 insertions(+), 166 deletions(-) create mode 100644 lib/ui/window/key_data_message.cc rename lib/ui/window/{key_data_packet.h => key_data_message.h} (54%) delete mode 100644 lib/ui/window/key_data_packet.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index f26e78aa510a8..6562b0cc6bdda 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -412,8 +412,8 @@ FILE: ../../../flutter/lib/ui/volatile_path_tracker.h FILE: ../../../flutter/lib/ui/window.dart FILE: ../../../flutter/lib/ui/window/key_data.cc FILE: ../../../flutter/lib/ui/window/key_data.h -FILE: ../../../flutter/lib/ui/window/key_data_packet.cc -FILE: ../../../flutter/lib/ui/window/key_data_packet.h +FILE: ../../../flutter/lib/ui/window/key_data_message.cc +FILE: ../../../flutter/lib/ui/window/key_data_message.h FILE: ../../../flutter/lib/ui/window/platform_configuration.cc FILE: ../../../flutter/lib/ui/window/platform_configuration.h FILE: ../../../flutter/lib/ui/window/platform_configuration_unittests.cc diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index 18521e8818a97..4156d7491a395 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -95,8 +95,8 @@ source_set("ui") { "volatile_path_tracker.h", "window/key_data.cc", "window/key_data.h", - "window/key_data_packet.cc", - "window/key_data_packet.h", + "window/key_data_message.cc", + "window/key_data_message.h", "window/platform_configuration.cc", "window/platform_configuration.h", "window/platform_message.cc", diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index 8d6770471ad71..cd1746f401d74 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -98,8 +98,8 @@ void _dispatchPointerDataPacket(ByteData packet) { @pragma('vm:entry-point') // ignore: unused_element -void _dispatchKeyData(ByteData packet) { - PlatformDispatcher.instance._dispatchKeyData(packet); +void _dispatchKeyData(ByteData packet, int responseId) { + PlatformDispatcher.instance._dispatchKeyData(packet, responseId); } @pragma('vm:entry-point') diff --git a/lib/ui/key.dart b/lib/ui/key.dart index 35cfd76501153..ba714c008d7fc 100644 --- a/lib/ui/key.dart +++ b/lib/ui/key.dart @@ -7,7 +7,6 @@ part of dart.ui; /// How the key has changed since the last report. -/// // Must match the KeyChange enum in ui/window/key_data.h. enum KeyChange { /// The key is pressed. @@ -34,8 +33,8 @@ class KeyData { /// Time of event dispatch, relative to an arbitrary timeline. /// - /// For [KeyChange.synchronize] and [KeyChange.cancel] events, the [timeStamp] - /// might not be the actual time that the key press or release happens. + /// For synthesized events, the [timeStamp] might not be the actual time that + /// the key press or release happens. final Duration timeStamp; /// How the key has changed since the last report. @@ -49,11 +48,35 @@ class KeyData { /// Character input from the event. /// - /// Not available to up events. + /// Ignored for up events. final String? character; - /// Whether the event is generated by Flutter, hence not corresponding to a - /// native event. + /// If [synthesized] is true, this event does not correspond to a native event. + /// + /// Although most of Flutter's keyboard events are transformed from native + /// events, some events are not based on native events, and are synthesized + /// only to conform Flutter's key event model (as documented in + /// the `HardwareKeyboard` class in the framework). + /// + /// For example, some key downs or ups might be lost when the window loses + /// focus. Some platforms provides ways to query whether a key is being held. + /// If Flutter detects an inconsistancy between the state Flutter records and + /// the state returned by the system, Flutter will synthesize a corresponding + /// event to synchronize the state without breaking the event model. + /// + /// As another example, macOS treats CapsLock in a special way by sending + /// down and up events at the down of alterate presses to indicate the + /// direction in which the lock is toggled instead of that the physical key is + /// going. Flutter normalizes the behavior by converting a native down event + /// into a down event followed immediately by a synthesized up event, and + /// the native up event also into a down event followed immediately by a + /// synthesized up event. + /// + /// Synthesized events do not have a trustworthy [timeStamp], and should not be + /// processed as if the key actually went down or up at the time of the + /// callback. + /// + /// [KeyRepeatEvent] is never synthesized. final bool synthesized; @override diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index c93fe976cc518..73faab867736e 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -28,8 +28,11 @@ typedef TimingsCallback = void Function(List timings); /// Signature for [PlatformDispatcher.onPointerDataPacket]. typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); +// Signature for the response to KeyDataCallback. +typedef _KeyDataResponseCallback = void Function(int responseId, bool handled); + /// Signature for [PlatformDispatcher.onKeyData]. -typedef KeyDataCallback = void Function(KeyData data); +typedef KeyDataCallback = bool Function(KeyData data); /// Signature for [PlatformDispatcher.onSemanticsAction]. typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); @@ -335,6 +338,10 @@ class PlatformDispatcher { return PointerDataPacket(data: data); } + /// Called by [_dispatchKeyData]. + void _respondToKeyData(int responseId, bool handled) + native 'PlatformConfiguration_respondToKeyData'; + /// A callback that is invoked when key data is available. /// /// The framework invokes this callback in the same zone in which the callback @@ -348,15 +355,15 @@ class PlatformDispatcher { } // Called from the engine, via hooks.dart - void _dispatchKeyData(ByteData packet) { - print('dispatch: onKeyData $onKeyData'); - if (onKeyData != null) { - _invoke1( - onKeyData, - _onKeyDataZone, - _unpackKeyData(packet), - ); - } + void _dispatchKeyData(ByteData packet, int responseId) { + _invoke2( + (KeyData data, _KeyDataResponseCallback callback) { + callback(responseId, onKeyData == null ? false : onKeyData!(data)); + }, + _onKeyDataZone, + _unpackKeyData(packet), + _respondToKeyData, + ); } // If this value changes, update the encoding code in the following files: @@ -368,6 +375,7 @@ class PlatformDispatcher { static const int _kKeyDataFieldCount = 5; // KeyData packet structure: + // | ResponseId | (1 field) // | CharDataSize | (1 field) // | Key Data | (_kKeyDataFieldCount fields) // | CharData | (CharDataSize bits) diff --git a/lib/ui/window/key_data.h b/lib/ui/window/key_data.h index d41c162f47f8a..783e9e6092a32 100644 --- a/lib/ui/window/key_data.h +++ b/lib/ui/window/key_data.h @@ -22,10 +22,10 @@ enum class KeyChange : int64_t { kRepeat, }; -// The fixed-length sections of a KeyDataPacket. +// The fixed-length sections of a KeyDataMessage. // -// KeyData does not contain `character`, for various-length data are stored in a -// different way in KeyDataPacket. +// KeyData does not contain `character`, for variable-length data are stored in a +// different way in KeyDataMessage. // // This structure is unpacked by hooks.dart. struct alignas(8) KeyData { diff --git a/lib/ui/window/key_data_message.cc b/lib/ui/window/key_data_message.cc new file mode 100644 index 0000000000000..cdcf6f25d9255 --- /dev/null +++ b/lib/ui/window/key_data_message.cc @@ -0,0 +1,29 @@ +// 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/lib/ui/window/key_data_message.h" +#include "flutter/fml/logging.h" + +namespace flutter { + +KeyDataMessage::KeyDataMessage(size_t character_data_size) + : data_(sizeof(uint64_t) + sizeof(KeyData) + character_data_size) { + uint64_t size64 = character_data_size; + memcpy(&data()[CharacterSizeStart_()], &size64, sizeof(size64)); +} + +KeyDataMessage::~KeyDataMessage() = default; + +void KeyDataMessage::SetKeyData(const KeyData& event) { + memcpy(&data()[KeyDataStart_()], &event, sizeof(KeyData)); +} + +void KeyDataMessage::SetCharacter(const char* character) { + if (character != nullptr) { + memcpy(data().data() + CharacterStart_(), character, + data().size() - CharacterStart_()); + } +} + +} // namespace flutter diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_message.h similarity index 54% rename from lib/ui/window/key_data_packet.h rename to lib/ui/window/key_data_message.h index fa48a60f7b676..ba77786505cac 100644 --- a/lib/ui/window/key_data_packet.h +++ b/lib/ui/window/key_data_message.h @@ -2,49 +2,29 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_PACKET_H_ -#define FLUTTER_LIB_UI_WINDOW_KEY_DATA_PACKET_H_ +#ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_MESSAGE_H_ +#define FLUTTER_LIB_UI_WINDOW_KEY_DATA_MESSAGE_H_ #include #include +#include #include "flutter/fml/macros.h" #include "flutter/lib/ui/window/key_data.h" namespace flutter { -// Bitstream that contains a KeyData. -// -// This class provides interface to read the opaque bits. For constructing such -// a bitstream from KeyData, checkout KeyDataPacketBuilder. -class KeyDataPacket { - public: - KeyDataPacket(uint8_t* data, size_t num_bytes); - - protected: - KeyDataPacket(size_t num_bytes); - - std::vector& data() { return data_; } +typedef std::function KeyDataMessageCallback; +class KeyDataMessage { public: - ~KeyDataPacket(); - - const std::vector& data() const { return data_; } - - private: - std::vector data_; - - FML_DISALLOW_COPY_AND_ASSIGN(KeyDataPacket); -}; - -// Build a KeyDataPacket bitstream gradually. -class KeyDataPacketBuilder : public KeyDataPacket { - public: - // Build a KeyDataPacket by incrementally fill in data. + // Build a KeyDataMessage by incrementally fill in data. // // The `character_data_size` is number of bytes to contain the character data. - KeyDataPacketBuilder(size_t character_data_size); - ~KeyDataPacketBuilder(); + KeyDataMessage(size_t character_data_size); + ~KeyDataMessage(); + + const std::vector& data() const { return data_; } void SetKeyData(const KeyData& event); @@ -53,13 +33,17 @@ class KeyDataPacketBuilder : public KeyDataPacket { void SetCharacter(const char* characters); private: + std::vector& data() { return data_; } + size_t CharacterSizeStart_() { return 0; } size_t KeyDataStart_() { return CharacterSizeStart_() + sizeof(uint64_t); } size_t CharacterStart_() { return KeyDataStart_() + sizeof(KeyData); } - FML_DISALLOW_COPY_AND_ASSIGN(KeyDataPacketBuilder); + std::vector data_; + + FML_DISALLOW_COPY_AND_ASSIGN(KeyDataMessage); }; } // namespace flutter -#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_PACKET_H_ +#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_MESSAGE_H_ diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc deleted file mode 100644 index 0a22b04f13d9d..0000000000000 --- a/lib/ui/window/key_data_packet.cc +++ /dev/null @@ -1,36 +0,0 @@ -// 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/lib/ui/window/key_data_packet.h" -#include "flutter/fml/logging.h" - -namespace flutter { - -KeyDataPacket::KeyDataPacket(uint8_t* data, size_t num_bytes) - : data_(data, data + num_bytes) {} - -KeyDataPacket::KeyDataPacket(size_t num_bytes) : data_(num_bytes) {} - -KeyDataPacket::~KeyDataPacket() = default; - -KeyDataPacketBuilder::KeyDataPacketBuilder(size_t character_data_size) - : KeyDataPacket(sizeof(uint64_t) + sizeof(KeyData) + character_data_size) { - uint64_t size64 = character_data_size; - memcpy(&data()[CharacterSizeStart_()], &size64, sizeof(size64)); -} - -KeyDataPacketBuilder::~KeyDataPacketBuilder() = default; - -void KeyDataPacketBuilder::SetKeyData(const KeyData& event) { - memcpy(&data()[KeyDataStart_()], &event, sizeof(KeyData)); -} - -void KeyDataPacketBuilder::SetCharacter(const char* character) { - if (character != nullptr) { - memcpy(data().data() + CharacterStart_(), character, - data().size() - CharacterStart_()); - } -} - -} // namespace flutter diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index ccf9cc877001f..230ed49826062 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -181,6 +181,16 @@ void GetPersistentIsolateData(Dart_NativeArguments args) { persistent_isolate_data->GetSize())); } +void RespondToKeyData(Dart_Handle window, int response_id, bool handled) { + UIDartState::Current() + ->platform_configuration() + ->CompleteKeyDataResponse(response_id, handled); +} + +void _RespondToKeyData(Dart_NativeArguments args) { + tonic::DartCallStatic(&RespondToKeyData, args); +} + Dart_Handle ToByteData(const std::vector& buffer) { return tonic::DartByteData::Create(buffer.data(), buffer.size()); } @@ -349,6 +359,14 @@ void PlatformConfiguration::DispatchSemanticsAction(int32_t id, args_handle})); } +uint64_t PlatformConfiguration::RegisterKeyDataResponse( + KeyDataMessageCallback callback) { + uint64_t response_id = next_key_response_id_++; + pending_key_responses_[response_id] = std::move(callback); + return response_id; +} + + void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime) { std::shared_ptr dart_state = begin_frame_.dart_state().lock(); @@ -424,6 +442,19 @@ void PlatformConfiguration::CompletePlatformMessageResponse( response->Complete(std::make_unique(std::move(data))); } +void PlatformConfiguration::CompleteKeyDataResponse(uint64_t response_id, bool handled) { + if (!response_id) { + return; + } + auto it = pending_key_responses_.find(response_id); + if (it == pending_key_responses_.end()) { + return; + } + auto callback = std::move(it->second); + pending_key_responses_.erase(it); + callback(handled); +} + Dart_Handle ComputePlatformResolvedLocale(Dart_Handle supportedLocalesHandle) { std::vector supportedLocales = tonic::DartConverter>::FromDart( @@ -454,6 +485,7 @@ void PlatformConfiguration::RegisterNatives( true}, {"PlatformConfiguration_respondToPlatformMessage", _RespondToPlatformMessage, 3, true}, + {"PlatformConfiguration_respondToKeyData", _RespondToKeyData, 2, true}, {"PlatformConfiguration_render", Render, 3, true}, {"PlatformConfiguration_updateSemantics", UpdateSemantics, 2, true}, {"PlatformConfiguration_setIsolateDebugName", SetIsolateDebugName, 2, diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index f61d47494ec1c..458c38134345c 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -323,6 +323,16 @@ class PlatformConfiguration final { SemanticsAction action, std::vector args); + //---------------------------------------------------------------------------- + /// @brief Notifies the PlatformConfiguration that the client has sent + /// it a message. This call originates in the platform view and + /// has been forwarded through the engine to here. + /// + /// @param[in] message The message sent from the embedder to the Dart + /// application. + /// + uint64_t RegisterKeyDataResponse(KeyDataMessageCallback callback); + //---------------------------------------------------------------------------- /// @brief Notifies the framework that it is time to begin working on a /// new frame previously scheduled via a call to @@ -411,6 +421,16 @@ class PlatformConfiguration final { /// void CompletePlatformMessageEmptyResponse(int response_id); + //---------------------------------------------------------------------------- + /// @brief Responds to a previous key data message to the engine from the + /// framework. + /// + /// @param[in] response_id The unique id that identifies the original platform + /// message to respond to. + /// @param[in] handled Whether the key is handled. + /// + void CompleteKeyDataResponse(uint64_t response_id, bool handled); + private: PlatformConfigurationClient* client_; tonic::DartPersistentValue update_locales_; @@ -419,6 +439,7 @@ class PlatformConfiguration final { tonic::DartPersistentValue update_semantics_enabled_; tonic::DartPersistentValue update_accessibility_features_; tonic::DartPersistentValue dispatch_platform_message_; + tonic::DartPersistentValue dispatch_key_message_; tonic::DartPersistentValue dispatch_semantics_action_; tonic::DartPersistentValue begin_frame_; tonic::DartPersistentValue draw_frame_; @@ -430,6 +451,11 @@ class PlatformConfiguration final { int next_response_id_ = 1; std::unordered_map> pending_responses_; + + // We use id 0 to mean that no response is expected. + uint64_t next_key_response_id_ = 1; + std::unordered_map + pending_key_responses_; }; } // namespace flutter diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index a6cbf7f35a40c..52ae9c0ed3a7f 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -36,20 +36,20 @@ void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) { library_.value(), "_dispatchPointerDataPacket", {data_handle})); } -void Window::DispatchKeyDataPacket(const KeyDataPacket& packet) { +void Window::DispatchKeyDataMessage(const KeyDataMessage& message, uint64_t response_id) { std::shared_ptr dart_state = library_.dart_state().lock(); if (!dart_state) return; tonic::DartState::Scope scope(dart_state); - const std::vector& buffer = packet.data(); + const std::vector buffer(message.data().size()); Dart_Handle data_handle = tonic::DartByteData::Create(buffer.data(), buffer.size()); if (Dart_IsError(data_handle)) { return; } tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_dispatchKeyData", - {data_handle})); + {data_handle, tonic::ToDart(response_id)})); } void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index d81e7e95aaa86..0573a8a579162 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -8,8 +8,9 @@ #include #include #include +#include -#include "flutter/lib/ui/window/key_data_packet.h" +#include "flutter/lib/ui/window/key_data_message.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/viewport_metrics.h" @@ -28,7 +29,7 @@ class Window final { const ViewportMetrics& viewport_metrics() const { return viewport_metrics_; } void DispatchPointerDataPacket(const PointerDataPacket& packet); - void DispatchKeyDataPacket(const KeyDataPacket& packet); + void DispatchKeyDataMessage(const KeyDataMessage& message, uint64_t response_id); void UpdateWindowMetrics(const ViewportMetrics& metrics); private: diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index 4107eed9978e0..ef35962121b7c 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -138,8 +138,6 @@ class KeyboardBinding { void _reset() { _clearListeners(); _converter.dispose(); - - _setup(); } } @@ -419,7 +417,7 @@ class KeyboardConverter { change = ui.KeyChange.up; } - late final int? nextLogicalRecord; + final int? nextLogicalRecord; switch (change) { case ui.KeyChange.down: assert(lastLogicalRecord == null); diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index 83ebe95725adf..c719e8fbbc18a 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -10,6 +10,8 @@ part of engine; /// This may be overridden in tests, for example, to pump fake frames. ui.VoidCallback? scheduleFrameCallback; +typedef _KeyDataResponseCallback = void Function(bool handled); + /// Platform event dispatcher. /// /// This is the central entry point for platform messages and configuration @@ -191,8 +193,11 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// Engine code should use this method instead of the callback directly. /// Otherwise zones won't work properly. - void invokeOnKeyData(ui.KeyData dataPacket) { - invoke1(_onKeyData, _onKeyDataZone, dataPacket); + void invokeOnKeyData(ui.KeyData data, _KeyDataResponseCallback callback) { + invoke( + () { callback(onKeyData == null ? false : onKeyData!(data)); }, + _onKeyDataZone, + ); } /// A callback that is invoked to report the [FrameTiming] of recently diff --git a/lib/web_ui/lib/src/ui/key.dart b/lib/web_ui/lib/src/ui/key.dart index ffb8d4e81064b..2e44a316552a8 100644 --- a/lib/web_ui/lib/src/ui/key.dart +++ b/lib/web_ui/lib/src/ui/key.dart @@ -7,6 +7,7 @@ part of ui; /// How the key has changed since the last report. +// Must match the KeyChange enum in ui/window/key_data.h. enum KeyChange { /// The key is pressed. down, @@ -47,20 +48,61 @@ class KeyData { /// Character input from the event. /// - /// Not available to up events. + /// Ignored for up events. final String? character; - /// Whether the event is generated by Flutter, hence not corresponding to a - /// native event. + /// If [synthesized] is true, this event does not correspond to a native event. + /// + /// Although most of Flutter's keyboard events are transformed from native + /// events, some events are not based on native events, and are synthesized + /// only to conform Flutter's key event model (as documented in + /// the `HardwareKeyboard` class in the framework). + /// + /// For example, some key downs or ups might be lost when the window loses + /// focus. Some platforms provides ways to query whether a key is being held. + /// If Flutter detects an inconsistancy between the state Flutter records and + /// the state returned by the system, Flutter will synthesize a corresponding + /// event to synchronize the state without breaking the event model. + /// + /// As another example, macOS treats CapsLock in a special way by sending + /// down and up events at the down of alterate presses to indicate the + /// direction in which the lock is toggled instead of that the physical key is + /// going. Flutter normalizes the behavior by converting a native down event + /// into a down event followed immediately by a synthesized up event, and + /// the native up event also into a down event followed immediately by a + /// synthesized up event. + /// + /// Synthesized events do not have a trustworthy [timeStamp], and should not be + /// processed as if the key actually went down or up at the time of the + /// callback. + /// + /// [KeyRepeatEvent] is never synthesized. final bool synthesized; @override - String toString() => 'KeyData(timeStamp: $timeStamp, change: $change, physical: $physical, ' - 'logical: $logical, character: $character, synthesized: $synthesized)'; + String toString() => 'KeyData(change: ${_changeToString(change)}, physical: 0x${physical.toRadixString(16)}, ' + 'logical: 0x${logical.toRadixString(16)}, character: $character)'; /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(' + 'change: ${_changeToString(change)}, ' + 'timeStamp: $timeStamp, ' + 'physical: 0x${physical.toRadixString(16)}, ' + 'logical: 0x${logical.toRadixString(16)}, ' + 'character: $character, ' + 'synthesized: $synthesized' ')'; } + + static String _changeToString(KeyChange change) { + switch (change) { + case KeyChange.up: + return 'up'; + case KeyChange.down: + return 'down'; + case KeyChange.repeat: + return 'repeat'; + } + } } diff --git a/lib/web_ui/lib/src/ui/platform_dispatcher.dart b/lib/web_ui/lib/src/ui/platform_dispatcher.dart index 96aec518b4f0b..87ee101c4d242 100644 --- a/lib/web_ui/lib/src/ui/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/ui/platform_dispatcher.dart @@ -9,7 +9,7 @@ typedef VoidCallback = void Function(); typedef FrameCallback = void Function(Duration duration); typedef TimingsCallback = void Function(List timings); typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); -typedef KeyDataCallback = void Function(KeyData packet); +typedef KeyDataCallback = bool Function(KeyData packet); typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); typedef PlatformMessageResponseCallback = void Function(ByteData? data); typedef PlatformMessageCallback = void Function( diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index b8a0327a393a9..049dc40847673 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -243,11 +243,14 @@ bool RuntimeController::DispatchPointerDataPacket( return false; } -bool RuntimeController::DispatchKeyDataPacket(const KeyDataPacket& packet) { +bool RuntimeController::DispatchKeyDataMessage( + const KeyDataMessage& message, + KeyDataMessageCallback callback) { if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { - TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataPacket", "mode", + TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataMessage", "mode", "basic"); - platform_configuration->get_window(0)->DispatchKeyDataPacket(packet); + uint64_t response_id = platform_configuration->RegisterKeyDataResponse(std::move(callback)); + platform_configuration->get_window(0)->DispatchKeyDataMessage(message, response_id); return true; } return false; diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 6f42f440c4b38..9a1a61fd51b43 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -414,7 +414,8 @@ class RuntimeController : public PlatformConfigurationClient { /// /// @param[in] packet The key data message to dispatch to the isolate. /// - bool DispatchKeyDataPacket(const KeyDataPacket& packet); + bool DispatchKeyDataMessage(const KeyDataMessage& message, + KeyDataMessageCallback callback); //---------------------------------------------------------------------------- /// @brief Dispatch the semantics action to the specified accessibility diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 6b49eab9badcd..3a6155bfd5c4a 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -384,10 +384,12 @@ void Engine::DispatchPointerDataPacket( pointer_data_dispatcher_->DispatchPacket(std::move(packet), trace_flow_id); } -void Engine::DispatchKeyDataPacket(std::unique_ptr packet) { - TRACE_EVENT0("flutter", "Engine::DispatchKeyDataPacket"); +void Engine::DispatchKeyDataMessage( + std::unique_ptr message, + KeyDataMessageCallback callback) { + TRACE_EVENT0("flutter", "Engine::DispatchKeyDataMessage"); if (runtime_controller_) { - runtime_controller_->DispatchKeyDataPacket(*packet); + runtime_controller_->DispatchKeyDataMessage(*message, std::move(callback)); } } diff --git a/shell/common/engine.h b/shell/common/engine.h index cf2234ac6c07d..5be4b5c8d995b 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -719,7 +719,8 @@ class Engine final : public RuntimeDelegate, /// /// @param[in] packet The key data packet. /// - void DispatchKeyDataPacket(std::unique_ptr packet); + void DispatchKeyDataMessage(std::unique_ptr message, + KeyDataMessageCallback callback); //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder encountered an diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 168087be13a35..2d8801daef0b6 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -42,9 +42,10 @@ void PlatformView::DispatchPointerDataPacket( pointer_data_packet_converter_.Convert(std::move(packet))); } -void PlatformView::DispatchKeyDataPacket( - std::unique_ptr packet) { - delegate_.OnPlatformViewDispatchKeyDataPacket(std::move(packet)); +void PlatformView::DispatchKeyDataMessage( + std::unique_ptr message, + KeyDataMessageCallback callback) { + delegate_.OnPlatformViewDispatchKeyDataMessage(std::move(message), std::move(callback)); } void PlatformView::DispatchSemanticsAction(int32_t id, diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index d8beccd37d6b1..b14635bc63cf5 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -16,7 +16,7 @@ #include "flutter/fml/memory/weak_ptr.h" #include "flutter/lib/ui/semantics/custom_accessibility_action.h" #include "flutter/lib/ui/semantics/semantics_node.h" -#include "flutter/lib/ui/window/key_data_packet.h" +#include "flutter/lib/ui/window/key_data_message.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/pointer_data_packet_converter.h" @@ -136,8 +136,9 @@ class PlatformView { /// event /// and multiple logical key events. /// - virtual void OnPlatformViewDispatchKeyDataPacket( - std::unique_ptr packet) = 0; + virtual void OnPlatformViewDispatchKeyDataMessage( + std::unique_ptr message, + KeyDataMessageCallback callback) = 0; //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view has encountered @@ -597,7 +598,8 @@ class PlatformView { /// /// @param[in] packet The key data packet to dispatch to the framework. /// - void DispatchKeyDataPacket(std::unique_ptr packet); + void DispatchKeyDataMessage(std::unique_ptr message, + KeyDataMessageCallback callback); //-------------------------------------------------------------------------- /// @brief Used by the embedder to specify a texture that it wants the diff --git a/shell/common/shell.cc b/shell/common/shell.cc index db0938ed8ce0b..46864ded709c0 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -876,16 +876,19 @@ void Shell::OnPlatformViewDispatchPointerDataPacket( } // |PlatformView::Delegate| -void Shell::OnPlatformViewDispatchKeyDataPacket( - std::unique_ptr packet) { - TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataPacket"); +void Shell::OnPlatformViewDispatchKeyDataMessage( + std::unique_ptr message, + KeyDataMessageCallback callback) { + TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataMessage"); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( - [engine = weak_engine_, packet = std::move(packet)]() mutable { + [engine = weak_engine_, + message = std::move(message), + callback = std::move(callback)]() mutable { if (engine) { - engine->DispatchKeyDataPacket(std::move(packet)); + engine->DispatchKeyDataMessage(std::move(message), std::move(callback)); } })); } diff --git a/shell/common/shell.h b/shell/common/shell.h index 480d8077daaa6..67bdb8d5c497b 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -457,8 +457,9 @@ class Shell final : public PlatformView::Delegate, std::unique_ptr packet) override; // |PlatformView::Delegate| - void OnPlatformViewDispatchKeyDataPacket( - std::unique_ptr packet) override; + void OnPlatformViewDispatchKeyDataMessage( + std::unique_ptr message, + KeyDataMessageCallback callback) override; // |PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction( diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index d32aad267d912..ee581fe07df57 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -62,8 +62,9 @@ class MockPlatformViewDelegate : public PlatformView::Delegate { MOCK_METHOD1(OnPlatformViewDispatchPointerDataPacket, void(std::unique_ptr packet)); - MOCK_METHOD1(OnPlatformViewDispatchKeyDataPacket, - void(std::unique_ptr packet)); + MOCK_METHOD2(OnPlatformViewDispatchKeyDataMessage, + void(std::unique_ptr message, + KeyDataMessageCallback callback)); MOCK_METHOD3(OnPlatformViewDispatchSemanticsAction, void(int32_t id, diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm index 129cce47c8199..552480face9d2 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm @@ -25,7 +25,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} + void OnPlatformViewDispatchKeyDataMessage(std::unique_ptr message) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 631f98c30c887..e99fa24fdd658 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -95,7 +95,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} + void OnPlatformViewDispatchKeyDataMessage(std::unique_ptr message) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index a446224495925..33ff62d5e17a6 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -78,7 +78,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} + void OnPlatformViewDispatchKeyDataMessage(std::unique_ptr message) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index f580eb8e67fc8..4d5917638148d 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1514,9 +1514,11 @@ inline flutter::KeyChange ToKeyChange(FlutterKeyEventKind key_change) { // i.e. 8 bytes. constexpr size_t kKeyEventCharacterMaxBytes = 8; -FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) - engine, - const FlutterKeyEvent* event) { +FlutterEngineResult FlutterEngineSendKeyEvent( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterKeyEvent* event, + FlutterKeyEventCallback callback, + void* user_data) { if (engine == nullptr) { return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); } @@ -1530,8 +1532,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) size_t character_data_size = character == nullptr ? 0 : strnlen(character, kKeyEventCharacterMaxBytes); - auto packet = - std::make_unique(character_data_size); + auto message = std::make_unique(character_data_size); flutter::KeyData key_data; key_data.Clear(); @@ -1540,12 +1541,18 @@ FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) SAFE_ACCESS(event, kind, FlutterKeyEventKind::kFlutterKeyEventKindUp)); key_data.physical = SAFE_ACCESS(event, physical, 0); key_data.logical = SAFE_ACCESS(event, logical, 0); - key_data.synthesized = !!SAFE_ACCESS(event, synthesized, false); - packet->SetKeyData(key_data); - packet->SetCharacter(character); + key_data.synthesized = SAFE_ACCESS(event, synthesized, false); + message->SetKeyData(key_data); + message->SetCharacter(character); + + auto response = [callback, user_data](bool handled) { + callback(handled, user_data); + }; return reinterpret_cast(engine) - ->DispatchKeyDataPacket(std::move(packet)) + ->DispatchKeyDataMessage( + std::move(message), + response) ? kSuccess : LOG_EMBEDDER_ERROR(kInternalInconsistency, "Could not dispatch the key event to the " diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 9f02e3cabebf3..19b534aa09f0f 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -617,29 +617,58 @@ typedef enum { kFlutterKeyEventKindRepeat, } FlutterKeyEventKind; +/// A structure to represent a change of state of a key. +/// +/// Sending `FlutterKeyEvent` via `FlutterEngineSendKeyEvent` results in a +/// corresponding `FlutterKeyEvent` to be dispatched in the framework. It is +/// embedder's responsibility to ensure the regularity of sent events, since the +/// framework only performs simple one-to-one mapping. The events must conform +/// the following rules: +/// +/// * Each key press sequence shall consist of one key down event (`kind` being +/// `kFlutterKeyEventKindDown`), zero or more repeat events, and one key up +/// event, representing a physical key button being pressed, held, and released. +/// * All events throughout a key press sequence shall have the same `physical` +/// and `logical`. Having different `character`s is allowed. typedef struct { /// The size of this struct. Must be sizeof(FlutterKeyEvent). size_t struct_size; - // Timestamp in microseconds. + /// The timestamp at which the pointer event was generated. The timestamp + /// should be specified in microseconds and the clock should be the same as + /// that used by `FlutterEngineGetCurrentTime`. double timestamp; - // The event kind. + /// The event kind. FlutterKeyEventKind kind; - // The physical key of the event, distinguished by the HID code. + /// The USB HID code for the physical key of the event. + /// + /// For the full definition and list of pre-defined physical keys, see + /// `PhysicalKeyboardKey` from the framework. uint64_t physical; - // The logical key of the event, usually the effect of the key without - // modifier, but might include modifier if the information is not available. - // Can be 0 for empty. + /// The key ID for the logical key of this event. + /// + /// For the full definition and a list of pre-defined logical keys, see + /// `LogicalKeyboardKey` from the framework. uint64_t logical; - // Null-terminated character input from the event. Can be null. Not available - // to up events. + /// Null-terminated character input from the event. Can be null. Ignored for + /// up events. const char* character; - // Whether the event is synthesized by Flutter. + /// True if this event does not correspond to a native event. + /// + /// The embedder is likely to skip events and/or construct new events that do + /// not correspond to any native events in order to conform the regularity + /// of events (as documented in `FlutterKeyEvent`). An example is when a key up + /// is missed due to loss of window focus, on a platform that provides query to + /// key pressing status, the embedder might realize that the key has been + /// released at the next key event, and should construct a synthesized up event + /// immediately before the actual event. + /// + /// An event being synthesized means that the framework will not trust the + /// `timestamp` of the event. bool synthesized; } FlutterKeyEvent; -struct _FlutterPlatformKeyEventResponseHandle; -typedef struct _FlutterPlatformKeyEventResponseHandle - FlutterPlatformKeyEventResponseHandle; +typedef void (*FlutterKeyEventCallback)(bool /* handled */, + void* /* user_data */); struct _FlutterPlatformMessageResponseHandle; typedef struct _FlutterPlatformMessageResponseHandle @@ -1579,11 +1608,30 @@ FlutterEngineResult FlutterEngineSendPointerEvent( const FlutterPointerEvent* events, size_t events_count); +//------------------------------------------------------------------------------ +/// @brief Send a key event to the engine, causing the framework to +/// dispatch a key event that is simply transformed from this one. +/// The framework will decide whether to handle this event in a +/// synchronous fashion, although due to technical limitation, the +/// result is always reported asynchronously. The `callback` is +/// guaranteed to be called exactly once. +/// +/// @param[in] engine A running engine instance. +/// @param[in] event The event data to be sent. This function will no +/// longer access `event` after returning. +/// @param[in] callback The callback invoked by the engine when the +/// Flutter application has decided whether it handles +/// this event. +/// @param[in] user_data The context associated with the callback. +/// +/// @return The result of the call. +/// FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendKeyEvent( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterKeyEvent* event, - FlutterPlatformKeyEventResponseHandle** response_out); + FlutterKeyEventCallback callback, + void* user_data); FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendPlatformMessage( @@ -2100,7 +2148,9 @@ typedef FlutterEngineResult (*FlutterEngineSendPointerEventFnPtr)( size_t events_count); typedef FlutterEngineResult (*FlutterEngineSendKeyEventFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, - const FlutterKeyEvent* event); + const FlutterKeyEvent* event, + FlutterKeyEventCallback callback, + void* user_data); typedef FlutterEngineResult (*FlutterEngineSendPlatformMessageFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage* message); diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index 32685c7b00ca0..852c26b957fc1 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -136,9 +136,10 @@ bool EmbedderEngine::DispatchPointerDataPacket( return true; } -bool EmbedderEngine::DispatchKeyDataPacket( - std::unique_ptr packet) { - if (!IsValid() || !packet) { +bool EmbedderEngine::DispatchKeyDataMessage( + std::unique_ptr message, + KeyDataMessageCallback callback) { + if (!IsValid() || !message) { return false; } @@ -147,7 +148,7 @@ bool EmbedderEngine::DispatchKeyDataPacket( return false; } - platform_view->DispatchKeyDataPacket(std::move(packet)); + platform_view->DispatchKeyDataMessage(std::move(message), std::move(callback)); return true; } diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index 53a7b315c219a..b41ca4d0df0a4 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -60,7 +60,8 @@ class EmbedderEngine { bool DispatchPointerDataPacket( std::unique_ptr packet); - bool DispatchKeyDataPacket(std::unique_ptr packet); + bool DispatchKeyDataMessage(std::unique_ptr message, + KeyDataMessageCallback callback); bool SendPlatformMessage(fml::RefPtr message); diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 1f6b793f1d160..a1d88d1298d8d 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -81,8 +81,8 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { void OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) {} // |flutter::PlatformView::Delegate| - void OnPlatformViewDispatchKeyDataPacket( - std::unique_ptr packet) {} + void OnPlatformViewDispatchKeyDataMessage( + std::unique_ptr message) {} // |flutter::PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction(int32_t id, flutter::SemanticsAction action, From 4be1c0f747b4a4bb444087a44da06f793cafeba7 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 19 Jan 2021 14:51:44 -0800 Subject: [PATCH 18/82] Rename back to KeyDataPacket --- ci/licenses_golden/licenses_flutter | 4 ++-- lib/ui/BUILD.gn | 4 ++-- lib/ui/window/key_data.h | 4 ++-- .../{key_data_message.cc => key_data_packet.cc} | 10 +++++----- .../window/{key_data_message.h => key_data_packet.h} | 12 ++++++------ lib/ui/window/platform_configuration.cc | 2 +- lib/ui/window/platform_configuration.h | 4 ++-- lib/ui/window/window.cc | 4 ++-- lib/ui/window/window.h | 4 ++-- runtime/runtime_controller.cc | 10 +++++----- runtime/runtime_controller.h | 4 ++-- shell/common/engine.cc | 10 +++++----- shell/common/engine.h | 4 ++-- shell/common/platform_view.cc | 8 ++++---- shell/common/platform_view.h | 12 ++++++------ shell/common/shell.cc | 12 ++++++------ shell/common/shell.h | 6 +++--- shell/common/shell_unittests.cc | 6 +++--- .../Source/FlutterEnginePlatformViewTest.mm | 2 +- .../ios/framework/Source/FlutterPlatformViewsTest.mm | 2 +- .../framework/Source/accessibility_bridge_test.mm | 2 +- shell/platform/embedder/embedder.cc | 4 ++-- shell/platform/embedder/embedder_engine.cc | 10 +++++----- shell/platform/embedder/embedder_engine.h | 4 ++-- .../fuchsia/flutter/platform_view_unittest.cc | 4 ++-- 25 files changed, 74 insertions(+), 74 deletions(-) rename lib/ui/window/{key_data_message.cc => key_data_packet.cc} (70%) rename lib/ui/window/{key_data_message.h => key_data_packet.h} (82%) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 6562b0cc6bdda..f26e78aa510a8 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -412,8 +412,8 @@ FILE: ../../../flutter/lib/ui/volatile_path_tracker.h FILE: ../../../flutter/lib/ui/window.dart FILE: ../../../flutter/lib/ui/window/key_data.cc FILE: ../../../flutter/lib/ui/window/key_data.h -FILE: ../../../flutter/lib/ui/window/key_data_message.cc -FILE: ../../../flutter/lib/ui/window/key_data_message.h +FILE: ../../../flutter/lib/ui/window/key_data_packet.cc +FILE: ../../../flutter/lib/ui/window/key_data_packet.h FILE: ../../../flutter/lib/ui/window/platform_configuration.cc FILE: ../../../flutter/lib/ui/window/platform_configuration.h FILE: ../../../flutter/lib/ui/window/platform_configuration_unittests.cc diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index 4156d7491a395..18521e8818a97 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -95,8 +95,8 @@ source_set("ui") { "volatile_path_tracker.h", "window/key_data.cc", "window/key_data.h", - "window/key_data_message.cc", - "window/key_data_message.h", + "window/key_data_packet.cc", + "window/key_data_packet.h", "window/platform_configuration.cc", "window/platform_configuration.h", "window/platform_message.cc", diff --git a/lib/ui/window/key_data.h b/lib/ui/window/key_data.h index 783e9e6092a32..0237350b1bf7e 100644 --- a/lib/ui/window/key_data.h +++ b/lib/ui/window/key_data.h @@ -22,10 +22,10 @@ enum class KeyChange : int64_t { kRepeat, }; -// The fixed-length sections of a KeyDataMessage. +// The fixed-length sections of a KeyDataPacket. // // KeyData does not contain `character`, for variable-length data are stored in a -// different way in KeyDataMessage. +// different way in KeyDataPacket. // // This structure is unpacked by hooks.dart. struct alignas(8) KeyData { diff --git a/lib/ui/window/key_data_message.cc b/lib/ui/window/key_data_packet.cc similarity index 70% rename from lib/ui/window/key_data_message.cc rename to lib/ui/window/key_data_packet.cc index cdcf6f25d9255..a049251a94e37 100644 --- a/lib/ui/window/key_data_message.cc +++ b/lib/ui/window/key_data_packet.cc @@ -2,24 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/lib/ui/window/key_data_message.h" +#include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/fml/logging.h" namespace flutter { -KeyDataMessage::KeyDataMessage(size_t character_data_size) +KeyDataPacket::KeyDataPacket(size_t character_data_size) : data_(sizeof(uint64_t) + sizeof(KeyData) + character_data_size) { uint64_t size64 = character_data_size; memcpy(&data()[CharacterSizeStart_()], &size64, sizeof(size64)); } -KeyDataMessage::~KeyDataMessage() = default; +KeyDataPacket::~KeyDataPacket() = default; -void KeyDataMessage::SetKeyData(const KeyData& event) { +void KeyDataPacket::SetKeyData(const KeyData& event) { memcpy(&data()[KeyDataStart_()], &event, sizeof(KeyData)); } -void KeyDataMessage::SetCharacter(const char* character) { +void KeyDataPacket::SetCharacter(const char* character) { if (character != nullptr) { memcpy(data().data() + CharacterStart_(), character, data().size() - CharacterStart_()); diff --git a/lib/ui/window/key_data_message.h b/lib/ui/window/key_data_packet.h similarity index 82% rename from lib/ui/window/key_data_message.h rename to lib/ui/window/key_data_packet.h index ba77786505cac..c3be35ab6f702 100644 --- a/lib/ui/window/key_data_message.h +++ b/lib/ui/window/key_data_packet.h @@ -14,15 +14,15 @@ namespace flutter { -typedef std::function KeyDataMessageCallback; +typedef std::function KeyDataPacketCallback; -class KeyDataMessage { +class KeyDataPacket { public: - // Build a KeyDataMessage by incrementally fill in data. + // Build a KeyDataPacket by incrementally fill in data. // // The `character_data_size` is number of bytes to contain the character data. - KeyDataMessage(size_t character_data_size); - ~KeyDataMessage(); + KeyDataPacket(size_t character_data_size); + ~KeyDataPacket(); const std::vector& data() const { return data_; } @@ -41,7 +41,7 @@ class KeyDataMessage { std::vector data_; - FML_DISALLOW_COPY_AND_ASSIGN(KeyDataMessage); + FML_DISALLOW_COPY_AND_ASSIGN(KeyDataPacket); }; } // namespace flutter diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 230ed49826062..7cc8edcf5becc 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -360,7 +360,7 @@ void PlatformConfiguration::DispatchSemanticsAction(int32_t id, } uint64_t PlatformConfiguration::RegisterKeyDataResponse( - KeyDataMessageCallback callback) { + KeyDataPacketCallback callback) { uint64_t response_id = next_key_response_id_++; pending_key_responses_[response_id] = std::move(callback); return response_id; diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 458c38134345c..fa1de9a2ad8bd 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -331,7 +331,7 @@ class PlatformConfiguration final { /// @param[in] message The message sent from the embedder to the Dart /// application. /// - uint64_t RegisterKeyDataResponse(KeyDataMessageCallback callback); + uint64_t RegisterKeyDataResponse(KeyDataPacketCallback callback); //---------------------------------------------------------------------------- /// @brief Notifies the framework that it is time to begin working on a @@ -454,7 +454,7 @@ class PlatformConfiguration final { // We use id 0 to mean that no response is expected. uint64_t next_key_response_id_ = 1; - std::unordered_map + std::unordered_map pending_key_responses_; }; diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 52ae9c0ed3a7f..630785b5fc3bc 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -36,13 +36,13 @@ void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) { library_.value(), "_dispatchPointerDataPacket", {data_handle})); } -void Window::DispatchKeyDataMessage(const KeyDataMessage& message, uint64_t response_id) { +void Window::DispatchKeyDataPacket(const KeyDataPacket& packet, uint64_t response_id) { std::shared_ptr dart_state = library_.dart_state().lock(); if (!dart_state) return; tonic::DartState::Scope scope(dart_state); - const std::vector buffer(message.data().size()); + const std::vector buffer(packet.data().size()); Dart_Handle data_handle = tonic::DartByteData::Create(buffer.data(), buffer.size()); if (Dart_IsError(data_handle)) { diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index 0573a8a579162..051caf0c76ccb 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -10,7 +10,7 @@ #include #include -#include "flutter/lib/ui/window/key_data_message.h" +#include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/viewport_metrics.h" @@ -29,7 +29,7 @@ class Window final { const ViewportMetrics& viewport_metrics() const { return viewport_metrics_; } void DispatchPointerDataPacket(const PointerDataPacket& packet); - void DispatchKeyDataMessage(const KeyDataMessage& message, uint64_t response_id); + void DispatchKeyDataPacket(const KeyDataPacket& packet, uint64_t response_id); void UpdateWindowMetrics(const ViewportMetrics& metrics); private: diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 049dc40847673..c8925cc3e3742 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -243,14 +243,14 @@ bool RuntimeController::DispatchPointerDataPacket( return false; } -bool RuntimeController::DispatchKeyDataMessage( - const KeyDataMessage& message, - KeyDataMessageCallback callback) { +bool RuntimeController::DispatchKeyDataPacket( + const KeyDataPacket& packet, + KeyDataPacketCallback callback) { if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { - TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataMessage", "mode", + TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataPacket", "mode", "basic"); uint64_t response_id = platform_configuration->RegisterKeyDataResponse(std::move(callback)); - platform_configuration->get_window(0)->DispatchKeyDataMessage(message, response_id); + platform_configuration->get_window(0)->DispatchKeyDataPacket(packet, response_id); return true; } return false; diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 9a1a61fd51b43..557f0e2293bd3 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -414,8 +414,8 @@ class RuntimeController : public PlatformConfigurationClient { /// /// @param[in] packet The key data message to dispatch to the isolate. /// - bool DispatchKeyDataMessage(const KeyDataMessage& message, - KeyDataMessageCallback callback); + bool DispatchKeyDataPacket(const KeyDataPacket& packet, + KeyDataPacketCallback callback); //---------------------------------------------------------------------------- /// @brief Dispatch the semantics action to the specified accessibility diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 3a6155bfd5c4a..c0258afe882b5 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -384,12 +384,12 @@ void Engine::DispatchPointerDataPacket( pointer_data_dispatcher_->DispatchPacket(std::move(packet), trace_flow_id); } -void Engine::DispatchKeyDataMessage( - std::unique_ptr message, - KeyDataMessageCallback callback) { - TRACE_EVENT0("flutter", "Engine::DispatchKeyDataMessage"); +void Engine::DispatchKeyDataPacket( + std::unique_ptr packet, + KeyDataPacketCallback callback) { + TRACE_EVENT0("flutter", "Engine::DispatchKeyDataPacket"); if (runtime_controller_) { - runtime_controller_->DispatchKeyDataMessage(*message, std::move(callback)); + runtime_controller_->DispatchKeyDataPacket(*packet, std::move(callback)); } } diff --git a/shell/common/engine.h b/shell/common/engine.h index 5be4b5c8d995b..3c1aa17aaf710 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -719,8 +719,8 @@ class Engine final : public RuntimeDelegate, /// /// @param[in] packet The key data packet. /// - void DispatchKeyDataMessage(std::unique_ptr message, - KeyDataMessageCallback callback); + void DispatchKeyDataPacket(std::unique_ptr packet, + KeyDataPacketCallback callback); //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder encountered an diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 2d8801daef0b6..e83e10dd42a03 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -42,10 +42,10 @@ void PlatformView::DispatchPointerDataPacket( pointer_data_packet_converter_.Convert(std::move(packet))); } -void PlatformView::DispatchKeyDataMessage( - std::unique_ptr message, - KeyDataMessageCallback callback) { - delegate_.OnPlatformViewDispatchKeyDataMessage(std::move(message), std::move(callback)); +void PlatformView::DispatchKeyDataPacket( + std::unique_ptr packet, + KeyDataPacketCallback callback) { + delegate_.OnPlatformViewDispatchKeyDataPacket(std::move(packet), std::move(callback)); } void PlatformView::DispatchSemanticsAction(int32_t id, diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index b14635bc63cf5..1e37d4006795e 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -16,7 +16,7 @@ #include "flutter/fml/memory/weak_ptr.h" #include "flutter/lib/ui/semantics/custom_accessibility_action.h" #include "flutter/lib/ui/semantics/semantics_node.h" -#include "flutter/lib/ui/window/key_data_message.h" +#include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/pointer_data_packet_converter.h" @@ -136,9 +136,9 @@ class PlatformView { /// event /// and multiple logical key events. /// - virtual void OnPlatformViewDispatchKeyDataMessage( - std::unique_ptr message, - KeyDataMessageCallback callback) = 0; + virtual void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet, + KeyDataPacketCallback callback) = 0; //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view has encountered @@ -598,8 +598,8 @@ class PlatformView { /// /// @param[in] packet The key data packet to dispatch to the framework. /// - void DispatchKeyDataMessage(std::unique_ptr message, - KeyDataMessageCallback callback); + void DispatchKeyDataPacket(std::unique_ptr packet, + KeyDataPacketCallback callback); //-------------------------------------------------------------------------- /// @brief Used by the embedder to specify a texture that it wants the diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 46864ded709c0..cbbbc3cd60d06 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -876,19 +876,19 @@ void Shell::OnPlatformViewDispatchPointerDataPacket( } // |PlatformView::Delegate| -void Shell::OnPlatformViewDispatchKeyDataMessage( - std::unique_ptr message, - KeyDataMessageCallback callback) { - TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataMessage"); +void Shell::OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet, + KeyDataPacketCallback callback) { + TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataPacket"); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( [engine = weak_engine_, - message = std::move(message), + packet = std::move(packet), callback = std::move(callback)]() mutable { if (engine) { - engine->DispatchKeyDataMessage(std::move(message), std::move(callback)); + engine->DispatchKeyDataPacket(std::move(packet), std::move(callback)); } })); } diff --git a/shell/common/shell.h b/shell/common/shell.h index 67bdb8d5c497b..6b75be235d3b3 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -457,9 +457,9 @@ class Shell final : public PlatformView::Delegate, std::unique_ptr packet) override; // |PlatformView::Delegate| - void OnPlatformViewDispatchKeyDataMessage( - std::unique_ptr message, - KeyDataMessageCallback callback) override; + void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet, + KeyDataPacketCallback callback) override; // |PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction( diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index ee581fe07df57..5a9b13fe026ad 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -62,9 +62,9 @@ class MockPlatformViewDelegate : public PlatformView::Delegate { MOCK_METHOD1(OnPlatformViewDispatchPointerDataPacket, void(std::unique_ptr packet)); - MOCK_METHOD2(OnPlatformViewDispatchKeyDataMessage, - void(std::unique_ptr message, - KeyDataMessageCallback callback)); + MOCK_METHOD2(OnPlatformViewDispatchKeyDataPacket, + void(std::unique_ptr packet, + KeyDataPacketCallback callback)); MOCK_METHOD3(OnPlatformViewDispatchSemanticsAction, void(int32_t id, diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm index 552480face9d2..129cce47c8199 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm @@ -25,7 +25,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataMessage(std::unique_ptr message) override {} + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index e99fa24fdd658..631f98c30c887 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -95,7 +95,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataMessage(std::unique_ptr message) override {} + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index 33ff62d5e17a6..a446224495925 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -78,7 +78,7 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataMessage(std::unique_ptr message) override {} + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 4d5917638148d..4d0cac898a231 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1532,7 +1532,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( size_t character_data_size = character == nullptr ? 0 : strnlen(character, kKeyEventCharacterMaxBytes); - auto message = std::make_unique(character_data_size); + auto message = std::make_unique(character_data_size); flutter::KeyData key_data; key_data.Clear(); @@ -1550,7 +1550,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( }; return reinterpret_cast(engine) - ->DispatchKeyDataMessage( + ->DispatchKeyDataPacket( std::move(message), response) ? kSuccess diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index 852c26b957fc1..4c14cd9a42a3e 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -136,10 +136,10 @@ bool EmbedderEngine::DispatchPointerDataPacket( return true; } -bool EmbedderEngine::DispatchKeyDataMessage( - std::unique_ptr message, - KeyDataMessageCallback callback) { - if (!IsValid() || !message) { +bool EmbedderEngine::DispatchKeyDataPacket( + std::unique_ptr packet, + KeyDataPacketCallback callback) { + if (!IsValid() || !packet) { return false; } @@ -148,7 +148,7 @@ bool EmbedderEngine::DispatchKeyDataMessage( return false; } - platform_view->DispatchKeyDataMessage(std::move(message), std::move(callback)); + platform_view->DispatchKeyDataPacket(std::move(packet), std::move(callback)); return true; } diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index b41ca4d0df0a4..f2ca2c68d5ad8 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -60,8 +60,8 @@ class EmbedderEngine { bool DispatchPointerDataPacket( std::unique_ptr packet); - bool DispatchKeyDataMessage(std::unique_ptr message, - KeyDataMessageCallback callback); + bool DispatchKeyDataPacket(std::unique_ptr packet, + KeyDataPacketCallback callback); bool SendPlatformMessage(fml::RefPtr message); diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index a1d88d1298d8d..1f6b793f1d160 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -81,8 +81,8 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { void OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) {} // |flutter::PlatformView::Delegate| - void OnPlatformViewDispatchKeyDataMessage( - std::unique_ptr message) {} + void OnPlatformViewDispatchKeyDataPacket( + std::unique_ptr packet) {} // |flutter::PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction(int32_t id, flutter::SemanticsAction action, From 15388e606a79238395d774f8d463dc09fff99b5b Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 20 Jan 2021 17:10:38 -0800 Subject: [PATCH 19/82] Unit test --- lib/ui/ui.dart | 1 - lib/ui/window/platform_configuration.cc | 1 + lib/ui/window/platform_configuration.h | 31 ++++-- runtime/runtime_controller.h | 9 +- shell/common/engine.h | 6 +- shell/common/platform_view.h | 14 +-- shell/platform/embedder/BUILD.gn | 1 + shell/platform/embedder/embedder.h | 4 +- shell/platform/embedder/embedder_engine.h | 2 +- shell/platform/embedder/fixtures/main.dart | 39 +++++++- .../tests/embedder_unittests_key_event.cc | 98 +++++++++++++++++++ 11 files changed, 182 insertions(+), 24 deletions(-) create mode 100644 shell/platform/embedder/tests/embedder_unittests_key_event.cc diff --git a/lib/ui/ui.dart b/lib/ui/ui.dart index f48b8162ba42c..23d85835c1d92 100644 --- a/lib/ui/ui.dart +++ b/lib/ui/ui.dart @@ -12,7 +12,6 @@ // @dart = 2.12 library dart.ui; -import 'dart:_internal' hide Symbol; // ignore: unused_import import 'dart:async'; import 'dart:collection' as collection; import 'dart:convert'; diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 7cc8edcf5becc..e8358ed22c868 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -447,6 +447,7 @@ void PlatformConfiguration::CompleteKeyDataResponse(uint64_t response_id, bool h return; } auto it = pending_key_responses_.find(response_id); + FML_DCHECK(it != pending_key_responses_.end()); if (it == pending_key_responses_.end()) { return; } diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index fa1de9a2ad8bd..8a9f30f9c04d5 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -324,12 +324,20 @@ class PlatformConfiguration final { std::vector args); //---------------------------------------------------------------------------- - /// @brief Notifies the PlatformConfiguration that the client has sent - /// it a message. This call originates in the platform view and - /// has been forwarded through the engine to here. + /// @brief Register a callback to be invoked when the framework has + /// decided whether to handle an event. This callback originates + /// in the platform view and has been forwarded through the engine + /// to here. /// - /// @param[in] message The message sent from the embedder to the Dart - /// application. + /// This method will move and store the `callback`, associate it + /// with a self-incrementing identifier, the response ID, then + /// return the ID, which is typically used by + /// Window::DispatchKeyDataPacket. + /// + /// @param[in] callback The callback to be registered. + /// + /// @return The response ID to be associated with the callback. Using this + /// ID in CompleteKeyDataResponse will invoke the callback. /// uint64_t RegisterKeyDataResponse(KeyDataPacketCallback callback); @@ -422,12 +430,17 @@ class PlatformConfiguration final { void CompletePlatformMessageEmptyResponse(int response_id); //---------------------------------------------------------------------------- - /// @brief Responds to a previous key data message to the engine from the - /// framework. + /// @brief Responds to a previously registered key data message from the + /// framework to the engine. + /// + /// For each response_id, this method should be called exactly + /// once. Responding to a response_id that has not been registered + /// or has been invoked will lead to a fatal error. /// /// @param[in] response_id The unique id that identifies the original platform - /// message to respond to. - /// @param[in] handled Whether the key is handled. + /// message to respond to, created by + /// RegisterKeyDataResponse. + /// @param[in] handled Whether the key data is handled. /// void CompleteKeyDataResponse(uint64_t response_id, bool handled); diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 72f05a4cf809c..1057eb35bb682 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -428,10 +428,15 @@ class RuntimeController : public PlatformConfigurationClient { /// @brief Dispatch the specified pointer data message to the running /// root isolate. /// - /// @param[in] packet The key data message to dispatch to the isolate. + /// @param[in] packet The key data message to dispatch to the isolate. + /// @param[in] callback Called when the framework has decided whether + /// to handle this key data. + /// + /// @return If the key data message was dispatched. This may fail is + /// an isolate is not running. /// bool DispatchKeyDataPacket(const KeyDataPacket& packet, - KeyDataPacketCallback callback); + KeyDataPacketCallback callback); //---------------------------------------------------------------------------- /// @brief Dispatch the semantics action to the specified accessibility diff --git a/shell/common/engine.h b/shell/common/engine.h index 02aada947ed87..ae86e330f5d29 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -735,10 +735,12 @@ class Engine final : public RuntimeDelegate, /// the platform view and the shell has forwarded the same to the /// engine on the UI task runner here. /// - /// @param[in] packet The key data packet. + /// @param[in] packet The key data packet. + /// @param[in] callback Called when the framework has decided whether + /// to handle this key data. /// void DispatchKeyDataPacket(std::unique_ptr packet, - KeyDataPacketCallback callback); + KeyDataPacketCallback callback); //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder encountered an diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 1e37d4006795e..eeba64c94f65f 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -128,13 +128,13 @@ class PlatformView { //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view has encountered - /// a key event. This key event needs to be forwarded to - /// the running root isolate hosted by the engine on the UI - /// thread. + /// a key event. This key event and the callback needs to be + /// forwarded to the running root isolate hosted by the engine + /// on the UI thread. /// - /// @param[in] packet The key data packet containing one physical key - /// event - /// and multiple logical key events. + /// @param[in] packet The key data packet containing one key event. + /// @param[in] callback Called when the framework has decided whether + /// to handle this key data. /// virtual void OnPlatformViewDispatchKeyDataPacket( std::unique_ptr packet, @@ -599,7 +599,7 @@ class PlatformView { /// @param[in] packet The key data packet to dispatch to the framework. /// void DispatchKeyDataPacket(std::unique_ptr packet, - KeyDataPacketCallback callback); + KeyDataPacketCallback callback); //-------------------------------------------------------------------------- /// @brief Used by the embedder to specify a texture that it wants the diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index 7cbba62bbc5c5..193ebc348bd3b 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -192,6 +192,7 @@ if (enable_unittests) { "tests/embedder_test_context_software.cc", "tests/embedder_test_context_software.h", "tests/embedder_unittests.cc", + "tests/embedder_unittests_key_event.cc", "tests/embedder_unittests_util.cc", ] diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 19b534aa09f0f..d13858c03065d 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -1622,7 +1622,9 @@ FlutterEngineResult FlutterEngineSendPointerEvent( /// @param[in] callback The callback invoked by the engine when the /// Flutter application has decided whether it handles /// this event. -/// @param[in] user_data The context associated with the callback. +/// @param[in] user_data The context associated with the callback. Should +/// not be released until `callback` is invoked. Can +/// be null. /// /// @return The result of the call. /// diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index f2ca2c68d5ad8..b25f76e9f5466 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -61,7 +61,7 @@ class EmbedderEngine { std::unique_ptr packet); bool DispatchKeyDataPacket(std::unique_ptr packet, - KeyDataPacketCallback callback); + KeyDataPacketCallback callback); bool SendPlatformMessage(fml::RefPtr message); diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 81fc2e0c35bd6..54494a97baeb3 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -12,6 +12,8 @@ import 'dart:ffi'; import 'dart:core'; import 'dart:convert'; +import '../../../../lib/ui/ui.dart'; + void main() {} @pragma('vm:entry-point') @@ -495,6 +497,41 @@ Picture CreateGradientBox(Size size) { return baseRecorder.endRecording(); } +void _echoKeyEvent( + int change, + int timestamp, + int physical, + int logical, + int charCode, + bool synthesized) + native 'EchoKeyEvent'; + +int _serializeKeyChange(KeyChange change) { + switch(change) { + case KeyChange.up: + return 1; + case KeyChange.down: + return 2; + case KeyChange.repeat: + return 3; + } +} + +@pragma('vm:entry-point') +void key_data_echo() async { // ignore: non_constant_identifier_names + PlatformDispatcher.instance.onKeyData = (KeyData data) { + _echoKeyEvent( + _serializeKeyChange(data.change), + data.timeStamp.inMilliseconds, + data.physical, + data.logical, + data.character == null ? 0 : data.character!.codeUnitAt(0), + data.synthesized, + ); + return true; + }; +} + @pragma('vm:entry-point') void render_gradient() { PlatformDispatcher.instance.onBeginFrame = (Duration duration) { @@ -587,7 +624,7 @@ void can_display_platform_view_with_pixel_ratio() { @pragma('vm:entry-point') void can_receive_locale_updates() { PlatformDispatcher.instance.onLocaleChanged = (){ - signalNativeCount(PlatformDispatcher.instance.locales!.length); + signalNativeCount(PlatformDispatcher.instance.locales.length); }; signalNativeTest(); } diff --git a/shell/platform/embedder/tests/embedder_unittests_key_event.cc b/shell/platform/embedder/tests/embedder_unittests_key_event.cc new file mode 100644 index 0000000000000..54044d2a7bd2c --- /dev/null +++ b/shell/platform/embedder/tests/embedder_unittests_key_event.cc @@ -0,0 +1,98 @@ +// 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/embedder/embedder.h" + +#include + +#include "flutter/testing/testing.h" + +#ifdef _WIN32 +// winbase.h defines GetCurrentTime as a macro. +#undef GetCurrentTime +#endif + +namespace flutter { +namespace testing { + +FlutterKeyEventKind unserializeKind(uint64_t kindInt) { + switch(kindInt) { + case 1: + return kFlutterKeyEventKindUp; + case 2: + return kFlutterKeyEventKindDown; + case 3: + return kFlutterKeyEventKindRepeat; + default: + FML_UNREACHABLE(); + return kFlutterKeyEventKindUp; + } +} + +TEST(EmbedderKeyEvent, CorrectlySerializeKeyData) { + auto message_latch = std::make_shared(); + char echoed_string[2] = "m\n"; // Dummy string that holds one char. + FlutterKeyEvent echoed_event; + + auto native_echo_event = [&message_latch](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + echoed_event.type = unserializeKind( + tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 1))); + echoed_event.timestamp = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 2)); + echoed_event.physical = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 3)); + echoed_event.logical = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 4)); + echoed_string[0] = (char)tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 5)); + echoed_event.synthesized = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 6)); + + message_latch->Signal(); + }; + + context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); + + auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext); + + // fml::AutoResetWaitableEvent latch; + // context.AddNativeCallback( + // "SignalNativeTest", CREATE_NATIVE_ENTRY(([&latch](Dart_NativeArguments) { + // }))); + + EmbedderConfigBuilder builder(context); + builder.SetSoftwareRendererConfig(); + builder.SetDartEntrypoint("key_data_echo"); + + auto engine = builder.LaunchEngine(); + ASSERT_TRUE(engine.is_valid()); + + const FlutterKeyEvent downEventUpperA { + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1, + .kind = kFlutterKeyEventKindDown, + .physical = 0x00070004, + .logical = 0x00000000061, + .character = "A", + .synthesized = false, + }; + FlutterEngineSendKeyEvent(engine, &downEventUpperA, + [](bool handled, void* user_data){ + }, + null); + message_latch.Wait(); + + EXPECT_EQ(echoed_event.timestamp, downEventUpperA.timestamp); + EXPECT_EQ(echoed_event.kind, downEventUpperA.kind); + EXPECT_EQ(echoed_event.physical, downEventUpperA.physical); + EXPECT_EQ(echoed_event.logical, downEventUpperA.logical); + EXPECT_STREQ(echoed_event.character, downEventUpperA.character); + EXPECT_EQ(echoed_event.synthesized, downEventUpperA.synthesized); +} + + +} // namespace testing +} // namespace flutter From 8baf82ba21a1c24dbec2a6724d7e0c92d8b72cbc Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 20 Jan 2021 17:46:05 -0800 Subject: [PATCH 20/82] Fix compile --- shell/platform/embedder/fixtures/main.dart | 4 +-- .../tests/embedder_unittests_key_event.cc | 32 ++++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 54494a97baeb3..1819f0035cd2c 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -12,8 +12,6 @@ import 'dart:ffi'; import 'dart:core'; import 'dart:convert'; -import '../../../../lib/ui/ui.dart'; - void main() {} @pragma('vm:entry-point') @@ -624,7 +622,7 @@ void can_display_platform_view_with_pixel_ratio() { @pragma('vm:entry-point') void can_receive_locale_updates() { PlatformDispatcher.instance.onLocaleChanged = (){ - signalNativeCount(PlatformDispatcher.instance.locales.length); + signalNativeCount(PlatformDispatcher.instance.locales!.length); }; signalNativeTest(); } diff --git a/shell/platform/embedder/tests/embedder_unittests_key_event.cc b/shell/platform/embedder/tests/embedder_unittests_key_event.cc index 54044d2a7bd2c..37358ec1ee53c 100644 --- a/shell/platform/embedder/tests/embedder_unittests_key_event.cc +++ b/shell/platform/embedder/tests/embedder_unittests_key_event.cc @@ -2,10 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/shell/platform/embedder/embedder.h" - #include +#include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/tests/embedder_config_builder.h" +#include "flutter/shell/platform/embedder/tests/embedder_test.h" +#include "third_party/dart/runtime/include/dart_api.h" +#include "third_party/tonic/converter/dart_converter.h" #include "flutter/testing/testing.h" #ifdef _WIN32 @@ -18,11 +22,11 @@ namespace testing { FlutterKeyEventKind unserializeKind(uint64_t kindInt) { switch(kindInt) { - case 1: + case 1: return kFlutterKeyEventKindUp; - case 2: + case 2: return kFlutterKeyEventKindDown; - case 3: + case 3: return kFlutterKeyEventKindRepeat; default: FML_UNREACHABLE(); @@ -30,14 +34,13 @@ FlutterKeyEventKind unserializeKind(uint64_t kindInt) { } } -TEST(EmbedderKeyEvent, CorrectlySerializeKeyData) { +TEST_F(EmbedderTest, CorrectlySerializeKeyData) { auto message_latch = std::make_shared(); - char echoed_string[2] = "m\n"; // Dummy string that holds one char. + char echoed_string[2] = "m"; // Dummy string that holds one char. FlutterKeyEvent echoed_event; - auto native_echo_event = [&message_latch](Dart_NativeArguments args) { - auto handle = Dart_GetNativeArgument(args, 0); - echoed_event.type = unserializeKind( + auto native_echo_event = [&](Dart_NativeArguments args) { + echoed_event.kind = unserializeKind( tonic::DartConverter::FromDart( Dart_GetNativeArgument(args, 1))); echoed_event.timestamp = tonic::DartConverter::FromDart( @@ -54,9 +57,8 @@ TEST(EmbedderKeyEvent, CorrectlySerializeKeyData) { message_latch->Signal(); }; - context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); - auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext); + context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); // fml::AutoResetWaitableEvent latch; // context.AddNativeCallback( @@ -79,11 +81,11 @@ TEST(EmbedderKeyEvent, CorrectlySerializeKeyData) { .character = "A", .synthesized = false, }; - FlutterEngineSendKeyEvent(engine, &downEventUpperA, + FlutterEngineSendKeyEvent(engine.get(), &downEventUpperA, [](bool handled, void* user_data){ }, - null); - message_latch.Wait(); + nullptr); + message_latch->Wait(); EXPECT_EQ(echoed_event.timestamp, downEventUpperA.timestamp); EXPECT_EQ(echoed_event.kind, downEventUpperA.kind); From d63c9f21212d1bd4d70c1e28352217cb8f637a80 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 20 Jan 2021 19:02:40 -0800 Subject: [PATCH 21/82] First unit test passed --- lib/ui/window/key_data_packet.cc | 8 ++--- lib/ui/window/key_data_packet.h | 2 -- lib/ui/window/platform_configuration.cc | 2 +- lib/ui/window/window.cc | 2 +- shell/platform/embedder/embedder.cc | 8 ++--- shell/platform/embedder/fixtures/main.dart | 3 +- .../tests/embedder_unittests_key_event.cc | 34 +++++++++++-------- 7 files changed, 31 insertions(+), 28 deletions(-) diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc index a049251a94e37..a476be497a3e7 100644 --- a/lib/ui/window/key_data_packet.cc +++ b/lib/ui/window/key_data_packet.cc @@ -10,19 +10,19 @@ namespace flutter { KeyDataPacket::KeyDataPacket(size_t character_data_size) : data_(sizeof(uint64_t) + sizeof(KeyData) + character_data_size) { uint64_t size64 = character_data_size; - memcpy(&data()[CharacterSizeStart_()], &size64, sizeof(size64)); + memcpy(&data_[CharacterSizeStart_()], &size64, sizeof(size64)); } KeyDataPacket::~KeyDataPacket() = default; void KeyDataPacket::SetKeyData(const KeyData& event) { - memcpy(&data()[KeyDataStart_()], &event, sizeof(KeyData)); + memcpy(&data_[KeyDataStart_()], &event, sizeof(KeyData)); } void KeyDataPacket::SetCharacter(const char* character) { if (character != nullptr) { - memcpy(data().data() + CharacterStart_(), character, - data().size() - CharacterStart_()); + memcpy(data_.data() + CharacterStart_(), character, + data_.size() - CharacterStart_()); } } diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h index c3be35ab6f702..735bebd1d2cc1 100644 --- a/lib/ui/window/key_data_packet.h +++ b/lib/ui/window/key_data_packet.h @@ -33,8 +33,6 @@ class KeyDataPacket { void SetCharacter(const char* characters); private: - std::vector& data() { return data_; } - size_t CharacterSizeStart_() { return 0; } size_t KeyDataStart_() { return CharacterSizeStart_() + sizeof(uint64_t); } size_t CharacterStart_() { return KeyDataStart_() + sizeof(KeyData); } diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index e8358ed22c868..0eee18ce4a8f5 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -486,7 +486,7 @@ void PlatformConfiguration::RegisterNatives( true}, {"PlatformConfiguration_respondToPlatformMessage", _RespondToPlatformMessage, 3, true}, - {"PlatformConfiguration_respondToKeyData", _RespondToKeyData, 2, true}, + {"PlatformConfiguration_respondToKeyData", _RespondToKeyData, 3, true}, {"PlatformConfiguration_render", Render, 3, true}, {"PlatformConfiguration_updateSemantics", UpdateSemantics, 2, true}, {"PlatformConfiguration_setIsolateDebugName", SetIsolateDebugName, 2, diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 630785b5fc3bc..650fe0b37bbec 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -42,7 +42,7 @@ void Window::DispatchKeyDataPacket(const KeyDataPacket& packet, uint64_t respons return; tonic::DartState::Scope scope(dart_state); - const std::vector buffer(packet.data().size()); + const std::vector& buffer = packet.data(); Dart_Handle data_handle = tonic::DartByteData::Create(buffer.data(), buffer.size()); if (Dart_IsError(data_handle)) { diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 4d0cac898a231..f2b0a09716baf 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1532,7 +1532,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( size_t character_data_size = character == nullptr ? 0 : strnlen(character, kKeyEventCharacterMaxBytes); - auto message = std::make_unique(character_data_size); + auto packet = std::make_unique(character_data_size); flutter::KeyData key_data; key_data.Clear(); @@ -1542,8 +1542,8 @@ FlutterEngineResult FlutterEngineSendKeyEvent( key_data.physical = SAFE_ACCESS(event, physical, 0); key_data.logical = SAFE_ACCESS(event, logical, 0); key_data.synthesized = SAFE_ACCESS(event, synthesized, false); - message->SetKeyData(key_data); - message->SetCharacter(character); + packet->SetKeyData(key_data); + packet->SetCharacter(character); auto response = [callback, user_data](bool handled) { callback(handled, user_data); @@ -1551,7 +1551,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( return reinterpret_cast(engine) ->DispatchKeyDataPacket( - std::move(message), + std::move(packet), response) ? kSuccess : LOG_EMBEDDER_ERROR(kInternalInconsistency, diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 1819f0035cd2c..c46a6d7153a84 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -520,7 +520,7 @@ void key_data_echo() async { // ignore: non_constant_identifier_names PlatformDispatcher.instance.onKeyData = (KeyData data) { _echoKeyEvent( _serializeKeyChange(data.change), - data.timeStamp.inMilliseconds, + data.timeStamp.inMicroseconds, data.physical, data.logical, data.character == null ? 0 : data.character!.codeUnitAt(0), @@ -528,6 +528,7 @@ void key_data_echo() async { // ignore: non_constant_identifier_names ); return true; }; + signalNativeTest(); } @pragma('vm:entry-point') diff --git a/shell/platform/embedder/tests/embedder_unittests_key_event.cc b/shell/platform/embedder/tests/embedder_unittests_key_event.cc index 37358ec1ee53c..270aa83611086 100644 --- a/shell/platform/embedder/tests/embedder_unittests_key_event.cc +++ b/shell/platform/embedder/tests/embedder_unittests_key_event.cc @@ -37,40 +37,44 @@ FlutterKeyEventKind unserializeKind(uint64_t kindInt) { TEST_F(EmbedderTest, CorrectlySerializeKeyData) { auto message_latch = std::make_shared(); char echoed_string[2] = "m"; // Dummy string that holds one char. - FlutterKeyEvent echoed_event; + FlutterKeyEvent echoed_event { + .character = echoed_string, + }; auto native_echo_event = [&](Dart_NativeArguments args) { echoed_event.kind = unserializeKind( tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 1))); + Dart_GetNativeArgument(args, 0))); echoed_event.timestamp = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 2)); + Dart_GetNativeArgument(args, 1)); echoed_event.physical = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 3)); + Dart_GetNativeArgument(args, 2)); echoed_event.logical = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 4)); + Dart_GetNativeArgument(args, 3)); echoed_string[0] = (char)tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 5)); + Dart_GetNativeArgument(args, 4)); echoed_event.synthesized = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 6)); + Dart_GetNativeArgument(args, 5)); message_latch->Signal(); }; - auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext); - context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); - - // fml::AutoResetWaitableEvent latch; - // context.AddNativeCallback( - // "SignalNativeTest", CREATE_NATIVE_ENTRY(([&latch](Dart_NativeArguments) { - // }))); - + auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext); EmbedderConfigBuilder builder(context); builder.SetSoftwareRendererConfig(); builder.SetDartEntrypoint("key_data_echo"); + context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); + + fml::AutoResetWaitableEvent ready; + context.AddNativeCallback( + "SignalNativeTest", + CREATE_NATIVE_ENTRY( + [&ready](Dart_NativeArguments args) { ready.Signal(); })); + auto engine = builder.LaunchEngine(); ASSERT_TRUE(engine.is_valid()); + ready.Wait(); const FlutterKeyEvent downEventUpperA { .struct_size = sizeof(FlutterKeyEvent), From 091765c1571b869ee8d4b6606c8c3d6c22c79aa3 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 20 Jan 2021 22:10:39 -0800 Subject: [PATCH 22/82] Complete tests --- shell/platform/embedder/BUILD.gn | 1 - shell/platform/embedder/embedder.h | 14 +- shell/platform/embedder/fixtures/main.dart | 3 +- .../embedder/tests/embedder_unittests.cc | 200 ++++++++++++++++++ .../tests/embedder_unittests_key_event.cc | 104 --------- 5 files changed, 209 insertions(+), 113 deletions(-) delete mode 100644 shell/platform/embedder/tests/embedder_unittests_key_event.cc diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index 193ebc348bd3b..7cbba62bbc5c5 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -192,7 +192,6 @@ if (enable_unittests) { "tests/embedder_test_context_software.cc", "tests/embedder_test_context_software.h", "tests/embedder_unittests.cc", - "tests/embedder_unittests_key_event.cc", "tests/embedder_unittests_util.cc", ] diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index d13858c03065d..a0f4588552460 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -618,7 +618,7 @@ typedef enum { } FlutterKeyEventKind; /// A structure to represent a change of state of a key. -/// +/// /// Sending `FlutterKeyEvent` via `FlutterEngineSendKeyEvent` results in a /// corresponding `FlutterKeyEvent` to be dispatched in the framework. It is /// embedder's responsibility to ensure the regularity of sent events, since the @@ -641,12 +641,12 @@ typedef struct { FlutterKeyEventKind kind; /// The USB HID code for the physical key of the event. /// - /// For the full definition and list of pre-defined physical keys, see + /// For the full definition and list of pre-defined physical keys, see /// `PhysicalKeyboardKey` from the framework. uint64_t physical; /// The key ID for the logical key of this event. /// - /// For the full definition and a list of pre-defined logical keys, see + /// For the full definition and a list of pre-defined logical keys, see /// `LogicalKeyboardKey` from the framework. uint64_t logical; /// Null-terminated character input from the event. Can be null. Ignored for @@ -1618,13 +1618,13 @@ FlutterEngineResult FlutterEngineSendPointerEvent( /// /// @param[in] engine A running engine instance. /// @param[in] event The event data to be sent. This function will no -/// longer access `event` after returning. +/// longer access `event` after returning. /// @param[in] callback The callback invoked by the engine when the /// Flutter application has decided whether it handles /// this event. -/// @param[in] user_data The context associated with the callback. Should -/// not be released until `callback` is invoked. Can -/// be null. +/// @param[in] user_data The context associated with the callback. The exact +/// same value will used to invoke `callback`. Accepts +/// nullptr or a non-pointer value. /// /// @return The result of the call. /// diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index c46a6d7153a84..d6afa3b68dcb4 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -515,6 +515,7 @@ int _serializeKeyChange(KeyChange change) { } } +// Echo the event data with `_echoKeyEvent`, and returns synthesized as handled. @pragma('vm:entry-point') void key_data_echo() async { // ignore: non_constant_identifier_names PlatformDispatcher.instance.onKeyData = (KeyData data) { @@ -526,7 +527,7 @@ void key_data_echo() async { // ignore: non_constant_identifier_names data.character == null ? 0 : data.character!.codeUnitAt(0), data.synthesized, ); - return true; + return data.synthesized; }; signalNativeTest(); } diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 2fe1cb19122d9..574faec8fa00f 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1191,5 +1191,205 @@ TEST_F(EmbedderTest, CanLaunchAndShutdownWithAValidElfSource) { engine.reset(); } +//------------------------------------------------------------------------------ +// Key Data +//------------------------------------------------------------------------------ + +typedef struct { + std::shared_ptr latch; + bool returned; +} KeyEventUserData; + +FlutterKeyEventKind unserializeKeyEventKind(uint64_t kindInt) { + switch(kindInt) { + case 1: + return kFlutterKeyEventKindUp; + case 2: + return kFlutterKeyEventKindDown; + case 3: + return kFlutterKeyEventKindRepeat; + default: + FML_UNREACHABLE(); + return kFlutterKeyEventKindUp; + } +} + +void expect_key_event_eq(const FlutterKeyEvent& subject, const FlutterKeyEvent& baseline) { + EXPECT_EQ(subject.timestamp, baseline.timestamp); + EXPECT_EQ(subject.kind, baseline.kind); + EXPECT_EQ(subject.physical, baseline.physical); + EXPECT_EQ(subject.logical, baseline.logical); + EXPECT_EQ(subject.synthesized, baseline.synthesized); +} + +TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { + auto message_latch = std::make_shared(); + uint64_t echoed_char; + FlutterKeyEvent echoed_event; + + auto native_echo_event = [&](Dart_NativeArguments args) { + echoed_event.kind = unserializeKeyEventKind( + tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 0))); + echoed_event.timestamp = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 1)); + echoed_event.physical = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 2)); + echoed_event.logical = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 3)); + echoed_char = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 4)); + echoed_event.synthesized = tonic::DartConverter::FromDart( + Dart_GetNativeArgument(args, 5)); + + message_latch->Signal(); + }; + + auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext); + EmbedderConfigBuilder builder(context); + builder.SetSoftwareRendererConfig(); + builder.SetDartEntrypoint("key_data_echo"); + fml::AutoResetWaitableEvent ready; + context.AddNativeCallback( + "SignalNativeTest", + CREATE_NATIVE_ENTRY( + [&ready](Dart_NativeArguments args) { ready.Signal(); })); + + context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); + + auto engine = builder.LaunchEngine(); + ASSERT_TRUE(engine.is_valid()); + ready.Wait(); + + // A normal down event + const FlutterKeyEvent downEventUpperA { + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1, + .kind = kFlutterKeyEventKindDown, + .physical = 0x00070004, + .logical = 0x00000000061, + .character = "A", + .synthesized = false, + }; + FlutterEngineSendKeyEvent(engine.get(), &downEventUpperA, + [](bool handled, void* user_data){}, nullptr); + message_latch->Wait(); + + expect_key_event_eq(echoed_event, downEventUpperA); + EXPECT_EQ(echoed_char, 0x41llu); + + // A repeat event with multi-byte character + const FlutterKeyEvent repeatEventWideChar { + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1000, + .kind = kFlutterKeyEventKindRepeat, + .physical = 0x00070005, + .logical = 0x00000000062, + .character = "∆", + .synthesized = false, + }; + FlutterEngineSendKeyEvent(engine.get(), &repeatEventWideChar, + [](bool handled, void* user_data){}, nullptr); + message_latch->Wait(); + + expect_key_event_eq(echoed_event, repeatEventWideChar); + EXPECT_EQ(echoed_char, 0x2206llu); + + // An up event with no character, synthesized + const FlutterKeyEvent upEvent { + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1000000, + .kind = kFlutterKeyEventKindUp, + .physical = 0x00070006, + .logical = 0x00000000063, + .character = nullptr, + .synthesized = true, + }; + FlutterEngineSendKeyEvent(engine.get(), &upEvent, + [](bool handled, void* user_data){}, nullptr); + message_latch->Wait(); + + expect_key_event_eq(echoed_event, upEvent); + EXPECT_EQ(echoed_char, 0llu); +} + +TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { + auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext); + EmbedderConfigBuilder builder(context); + builder.SetSoftwareRendererConfig(); + builder.SetDartEntrypoint("key_data_echo"); + fml::AutoResetWaitableEvent ready; + context.AddNativeCallback( + "SignalNativeTest", + CREATE_NATIVE_ENTRY( + [&ready](Dart_NativeArguments args) { ready.Signal(); })); + + context.AddNativeCallback("EchoKeyEvent", + CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {})); + + auto engine = builder.LaunchEngine(); + ASSERT_TRUE(engine.is_valid()); + ready.Wait(); + + // Dispatch a single event + FlutterKeyEvent event { + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1000, + .kind = kFlutterKeyEventKindDown, + .physical = 0x00070005, + .logical = 0x00000000062, + .character = nullptr, + }; + + KeyEventUserData user_data1 { + .latch = std::make_shared(), + }; + // Entrypoint `key_data_echo` uses `event.synthesized` as `handled`. + event.synthesized = true; + FlutterEngineSendKeyEvent( + engine.get(), + &event, + [](bool handled, void* untyped_user_data){ + KeyEventUserData* user_data = reinterpret_cast(untyped_user_data); + EXPECT_EQ(handled, true); + user_data->latch->Signal(); + }, + &user_data1); + user_data1.latch->Wait(); + + // Dispatch two events back to back, using the same callback on different + // user_data + KeyEventUserData user_data2 { + .latch = std::make_shared(), + .returned = false, + }; + KeyEventUserData user_data3 { + .latch = std::make_shared(), + .returned = false, + }; + auto callback23 = [](bool handled, void* untyped_user_data){ + KeyEventUserData* user_data = reinterpret_cast(untyped_user_data); + EXPECT_EQ(handled, false); + user_data->latch->Signal(); + user_data->returned = true; + }; + + event.synthesized = false; + FlutterEngineSendKeyEvent( + engine.get(), + &event, + callback23, + &user_data2); + FlutterEngineSendKeyEvent( + engine.get(), + &event, + callback23, + &user_data3); + user_data2.latch->Wait(); + user_data3.latch->Wait(); + EXPECT_TRUE(user_data2.returned); + EXPECT_TRUE(user_data3.returned); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/embedder/tests/embedder_unittests_key_event.cc b/shell/platform/embedder/tests/embedder_unittests_key_event.cc deleted file mode 100644 index 270aa83611086..0000000000000 --- a/shell/platform/embedder/tests/embedder_unittests_key_event.cc +++ /dev/null @@ -1,104 +0,0 @@ -// 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 - -#include "flutter/fml/synchronization/waitable_event.h" -#include "flutter/shell/platform/embedder/embedder.h" -#include "flutter/shell/platform/embedder/tests/embedder_config_builder.h" -#include "flutter/shell/platform/embedder/tests/embedder_test.h" -#include "third_party/dart/runtime/include/dart_api.h" -#include "third_party/tonic/converter/dart_converter.h" -#include "flutter/testing/testing.h" - -#ifdef _WIN32 -// winbase.h defines GetCurrentTime as a macro. -#undef GetCurrentTime -#endif - -namespace flutter { -namespace testing { - -FlutterKeyEventKind unserializeKind(uint64_t kindInt) { - switch(kindInt) { - case 1: - return kFlutterKeyEventKindUp; - case 2: - return kFlutterKeyEventKindDown; - case 3: - return kFlutterKeyEventKindRepeat; - default: - FML_UNREACHABLE(); - return kFlutterKeyEventKindUp; - } -} - -TEST_F(EmbedderTest, CorrectlySerializeKeyData) { - auto message_latch = std::make_shared(); - char echoed_string[2] = "m"; // Dummy string that holds one char. - FlutterKeyEvent echoed_event { - .character = echoed_string, - }; - - auto native_echo_event = [&](Dart_NativeArguments args) { - echoed_event.kind = unserializeKind( - tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 0))); - echoed_event.timestamp = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 1)); - echoed_event.physical = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 2)); - echoed_event.logical = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 3)); - echoed_string[0] = (char)tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 4)); - echoed_event.synthesized = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 5)); - - message_latch->Signal(); - }; - - auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext); - EmbedderConfigBuilder builder(context); - builder.SetSoftwareRendererConfig(); - builder.SetDartEntrypoint("key_data_echo"); - - context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); - - fml::AutoResetWaitableEvent ready; - context.AddNativeCallback( - "SignalNativeTest", - CREATE_NATIVE_ENTRY( - [&ready](Dart_NativeArguments args) { ready.Signal(); })); - - auto engine = builder.LaunchEngine(); - ASSERT_TRUE(engine.is_valid()); - ready.Wait(); - - const FlutterKeyEvent downEventUpperA { - .struct_size = sizeof(FlutterKeyEvent), - .timestamp = 1, - .kind = kFlutterKeyEventKindDown, - .physical = 0x00070004, - .logical = 0x00000000061, - .character = "A", - .synthesized = false, - }; - FlutterEngineSendKeyEvent(engine.get(), &downEventUpperA, - [](bool handled, void* user_data){ - }, - nullptr); - message_latch->Wait(); - - EXPECT_EQ(echoed_event.timestamp, downEventUpperA.timestamp); - EXPECT_EQ(echoed_event.kind, downEventUpperA.kind); - EXPECT_EQ(echoed_event.physical, downEventUpperA.physical); - EXPECT_EQ(echoed_event.logical, downEventUpperA.logical); - EXPECT_STREQ(echoed_event.character, downEventUpperA.character); - EXPECT_EQ(echoed_event.synthesized, downEventUpperA.synthesized); -} - - -} // namespace testing -} // namespace flutter From f4c7b411153e8c8de61fe973d2653ae9fbf28c2f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 13:04:55 -0800 Subject: [PATCH 23/82] Rename to type --- lib/ui/key.dart | 26 ++-- lib/ui/platform_dispatcher.dart | 3 +- lib/ui/window/key_data.h | 6 +- .../lib/src/engine/keyboard_binding.dart | 24 ++-- lib/web_ui/lib/src/ui/key.dart | 18 +-- lib/web_ui/test/keyboard_converter_test.dart | 124 +++++++++--------- shell/common/fixtures/shell_test.dart | 2 +- shell/platform/embedder/embedder.cc | 12 +- shell/platform/embedder/fixtures/main.dart | 10 +- 9 files changed, 112 insertions(+), 113 deletions(-) diff --git a/lib/ui/key.dart b/lib/ui/key.dart index ba714c008d7fc..19641476bd1ed 100644 --- a/lib/ui/key.dart +++ b/lib/ui/key.dart @@ -7,8 +7,8 @@ part of dart.ui; /// How the key has changed since the last report. -// Must match the KeyChange enum in ui/window/key_data.h. -enum KeyChange { +// Must match the KeyEventType enum in ui/window/key_data.h. +enum KeyEventType { /// The key is pressed. down, @@ -19,12 +19,12 @@ enum KeyChange { repeat, } -/// Information about the change of a key. +/// Information about a key event. class KeyData { /// Creates an object that represents the change of a key. const KeyData({ required this.timeStamp, - required this.change, + required this.type, required this.physical, required this.logical, required this.character, @@ -37,8 +37,8 @@ class KeyData { /// the key press or release happens. final Duration timeStamp; - /// How the key has changed since the last report. - final KeyChange change; + /// The type of the event. + final KeyEventType type; /// The key code for the physical key that has changed. final int physical; @@ -64,7 +64,7 @@ class KeyData { /// the state returned by the system, Flutter will synthesize a corresponding /// event to synchronize the state without breaking the event model. /// - /// As another example, macOS treats CapsLock in a special way by sending + /// As another example, macOS treats CapsLock in a special way by sending /// down and up events at the down of alterate presses to indicate the /// direction in which the lock is toggled instead of that the physical key is /// going. Flutter normalizes the behavior by converting a native down event @@ -80,13 +80,13 @@ class KeyData { final bool synthesized; @override - String toString() => 'KeyData(change: ${_changeToString(change)}, physical: 0x${physical.toRadixString(16)}, ' + String toString() => 'KeyData(change: ${_changeToString(type)}, physical: 0x${physical.toRadixString(16)}, ' 'logical: 0x${logical.toRadixString(16)}, character: $character)'; /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(' - 'change: ${_changeToString(change)}, ' + 'change: ${_changeToString(type)}, ' 'timeStamp: $timeStamp, ' 'physical: 0x${physical.toRadixString(16)}, ' 'logical: 0x${logical.toRadixString(16)}, ' @@ -95,13 +95,13 @@ class KeyData { ')'; } - static String _changeToString(KeyChange change) { + static String _changeToString(KeyEventType change) { switch (change) { - case KeyChange.up: + case KeyEventType.up: return 'up'; - case KeyChange.down: + case KeyEventType.down: return 'down'; - case KeyChange.repeat: + case KeyEventType.repeat: return 'repeat'; } } diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 73faab867736e..703486093cee5 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -375,7 +375,6 @@ class PlatformDispatcher { static const int _kKeyDataFieldCount = 5; // KeyData packet structure: - // | ResponseId | (1 field) // | CharDataSize | (1 field) // | Key Data | (_kKeyDataFieldCount fields) // | CharData | (CharDataSize bits) @@ -389,7 +388,7 @@ class PlatformDispatcher { final KeyData keyData = KeyData( timeStamp: Duration(microseconds: packet.getUint64(kStride * offset++, _kFakeHostEndian)), - change: KeyChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], + type: KeyEventType.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], physical: packet.getUint64(kStride * offset++, _kFakeHostEndian), logical: packet.getUint64(kStride * offset++, _kFakeHostEndian), character: character, diff --git a/lib/ui/window/key_data.h b/lib/ui/window/key_data.h index 0237350b1bf7e..1dd1892acab6c 100644 --- a/lib/ui/window/key_data.h +++ b/lib/ui/window/key_data.h @@ -15,8 +15,8 @@ static constexpr int kBytesPerKeyField = sizeof(int64_t); // The change of the key event, used by KeyData. // -// Must match the KeyChange enum in ui/key.dart. -enum class KeyChange : int64_t { +// Must match the KeyEventType enum in ui/key.dart. +enum class KeyEventType : int64_t { kDown = 0, kUp, kRepeat, @@ -31,7 +31,7 @@ enum class KeyChange : int64_t { struct alignas(8) KeyData { // Timestamp in microseconds from an arbitrary and consistant start point uint64_t timestamp; - KeyChange change; + KeyEventType change; uint64_t physical; uint64_t logical; // True if the event does not correspond to a native event. diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index ef35962121b7c..1a373e66a20bf 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -302,7 +302,7 @@ class KeyboardConverter { _keydownCancelDuration, () => ui.KeyData( timeStamp: currentTimeStamp + _keydownCancelDuration, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: physicalKey, logical: logicalKey, character: null, @@ -362,7 +362,7 @@ class KeyboardConverter { final int? lastLogicalRecord = _pressingRecords[physicalKey]; - ui.KeyChange change; + ui.KeyEventType change; if (_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock) { // Case 1: Handle CapsLock on macOS @@ -374,7 +374,7 @@ class KeyboardConverter { Duration.zero, () => ui.KeyData( timeStamp: timeStamp, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: physicalKey, logical: logicalKey, character: null, @@ -384,16 +384,16 @@ class KeyboardConverter { _pressingRecords.remove(physicalKey); } ); - change = ui.KeyChange.down; + change = ui.KeyEventType.down; } else if (isPhysicalDown) { // Case 2: Handle key down of normal keys - change = ui.KeyChange.down; + change = ui.KeyEventType.down; if (lastLogicalRecord != null) { // This physical key is being pressed according to the record. if (event.repeat ?? false) { // A normal repeated key. - change = ui.KeyChange.repeat; + change = ui.KeyEventType.repeat; } else { // A non-repeated key has been pressed that has the exact physical key as // a currently pressed one, usually indicating multiple keyboards are @@ -414,20 +414,20 @@ class KeyboardConverter { return false; } - change = ui.KeyChange.up; + change = ui.KeyEventType.up; } final int? nextLogicalRecord; switch (change) { - case ui.KeyChange.down: + case ui.KeyEventType.down: assert(lastLogicalRecord == null); nextLogicalRecord = logicalKey; break; - case ui.KeyChange.up: + case ui.KeyEventType.up: assert(lastLogicalRecord != null); nextLogicalRecord = null; break; - case ui.KeyChange.repeat: + case ui.KeyEventType.repeat: assert(lastLogicalRecord != null); nextLogicalRecord = lastLogicalRecord; break; @@ -451,7 +451,7 @@ class KeyboardConverter { dispatchKeyData(ui.KeyData( timeStamp: timeStamp, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: physicalKey, logical: logicalKey, character: null, @@ -477,7 +477,7 @@ class KeyboardConverter { change: change, physical: physicalKey, logical: lastLogicalRecord ?? logicalKey, - character: change == ui.KeyChange.up ? null : character, + character: change == ui.KeyEventType.up ? null : character, synthesized: false, ); diff --git a/lib/web_ui/lib/src/ui/key.dart b/lib/web_ui/lib/src/ui/key.dart index 2e44a316552a8..9ae86116ca6a2 100644 --- a/lib/web_ui/lib/src/ui/key.dart +++ b/lib/web_ui/lib/src/ui/key.dart @@ -7,8 +7,8 @@ part of ui; /// How the key has changed since the last report. -// Must match the KeyChange enum in ui/window/key_data.h. -enum KeyChange { +// Must match the KeyEventType enum in ui/window/key_data.h. +enum KeyEventType { /// The key is pressed. down, @@ -33,12 +33,12 @@ class KeyData { /// Time of event dispatch, relative to an arbitrary timeline. /// - /// For [KeyChange.synchronize] and [KeyChange.cancel] events, the [timeStamp] + /// For [KeyEventType.synchronize] and [KeyEventType.cancel] events, the [timeStamp] /// might not be the actual time that the key press or release happens. final Duration timeStamp; /// How the key has changed since the last report. - final KeyChange change; + final KeyEventType change; /// The key code for the physical key that has changed. final int physical; @@ -64,7 +64,7 @@ class KeyData { /// the state returned by the system, Flutter will synthesize a corresponding /// event to synchronize the state without breaking the event model. /// - /// As another example, macOS treats CapsLock in a special way by sending + /// As another example, macOS treats CapsLock in a special way by sending /// down and up events at the down of alterate presses to indicate the /// direction in which the lock is toggled instead of that the physical key is /// going. Flutter normalizes the behavior by converting a native down event @@ -95,13 +95,13 @@ class KeyData { ')'; } - static String _changeToString(KeyChange change) { + static String _changeToString(KeyEventType change) { switch (change) { - case KeyChange.up: + case KeyEventType.up: return 'up'; - case KeyChange.down: + case KeyEventType.down: return 'down'; - case KeyChange.repeat: + case KeyEventType.repeat: return 'repeat'; } } diff --git a/lib/web_ui/test/keyboard_converter_test.dart b/lib/web_ui/test/keyboard_converter_test.dart index 3b0003a1edbc8..daa09e9dd89c5 100644 --- a/lib/web_ui/test/keyboard_converter_test.dart +++ b/lib/web_ui/test/keyboard_converter_test.dart @@ -59,7 +59,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(milliseconds: 1), - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -72,7 +72,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(milliseconds: 1, microseconds: 500), - change: ui.KeyChange.repeat, + change: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -85,7 +85,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(seconds: 1, milliseconds: 500), - change: ui.KeyChange.repeat, + change: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -98,7 +98,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(seconds: 2, microseconds: 500), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -114,7 +114,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -122,7 +122,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'A', kShift)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'A', @@ -130,7 +130,7 @@ void testMain() { converter.handleEvent(keyRepeatedDownEvent('KeyA', 'A', kShift)); expectKeyData(keyDataList.last, - change: ui.KeyChange.repeat, + change: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'A', @@ -138,7 +138,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -146,7 +146,7 @@ void testMain() { converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyChange.repeat, + change: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -154,7 +154,7 @@ void testMain() { converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyChange.repeat, + change: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -162,7 +162,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -177,7 +177,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -185,7 +185,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, @@ -193,7 +193,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -201,7 +201,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, @@ -216,7 +216,7 @@ void testMain() { converter.handleEvent(keyDownEvent('Digit1', '1', 0, 0)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalDigit1, logical: kLogicalDigit1, character: '1', @@ -224,7 +224,7 @@ void testMain() { converter.handleEvent(keyDownEvent('Numpad1', '1', 0, kLocationNumpad)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalNumpad1, logical: kLogicalNumpad1, character: '1', @@ -232,7 +232,7 @@ void testMain() { converter.handleEvent(keyUpEvent('Digit1', '1', 0, 0)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalDigit1, logical: kLogicalDigit1, character: null, @@ -240,7 +240,7 @@ void testMain() { converter.handleEvent(keyUpEvent('Numpad1', '1', 0, kLocationNumpad)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalNumpad1, logical: kLogicalNumpad1, character: null, @@ -257,7 +257,7 @@ void testMain() { converter.handleEvent(keyDownEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalTab, logical: kLogicalTab, character: null, @@ -267,7 +267,7 @@ void testMain() { converter.handleEvent(keyUpEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalTab, logical: kLogicalTab, character: null, @@ -294,7 +294,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyE, logical: kLogicalAltE, character: null, @@ -302,7 +302,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyE', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyE, logical: kLogicalAltE, character: null, @@ -310,7 +310,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyU', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyU, logical: kLogicalAltU, character: null, @@ -318,7 +318,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyU', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyU, logical: kLogicalAltU, character: null, @@ -330,7 +330,7 @@ void testMain() { // testing. converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt | kShift)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyE, logical: kLogicalAltShiftE, character: null, @@ -340,7 +340,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyE', 'e', kShift)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyE, logical: kLogicalAltShiftE, character: null, @@ -365,7 +365,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -405,7 +405,7 @@ void testMain() { keyDataList.clear(); converter.handleEvent(keyDownEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -413,7 +413,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyU', 'u')); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyU, logical: kLogicalKeyU, character: 'u', @@ -429,7 +429,7 @@ void testMain() { converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -439,7 +439,7 @@ void testMain() { async.elapse(Duration(microseconds: 1)); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -450,7 +450,7 @@ void testMain() { converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -460,7 +460,7 @@ void testMain() { async.elapse(Duration(microseconds: 1)); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -472,7 +472,7 @@ void testMain() { converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -495,7 +495,7 @@ void testMain() { converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -508,7 +508,7 @@ void testMain() { converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -520,7 +520,7 @@ void testMain() { converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -528,7 +528,7 @@ void testMain() { converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -547,7 +547,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a', kMeta)..timeStamp = 200); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 200), - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -559,7 +559,7 @@ void testMain() { async.elapse(Duration(milliseconds: 2500)); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 1200), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -570,7 +570,7 @@ void testMain() { converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 2700); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2700), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalMetaLeft, logical: kLogicalMetaLeft, character: null, @@ -581,7 +581,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2800), - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -591,7 +591,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2900), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -621,7 +621,7 @@ void testMain() { async.elapse(Duration(milliseconds: 2500)); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 1700), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -632,7 +632,7 @@ void testMain() { converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 3200); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 3200), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalMetaLeft, logical: kLogicalMetaLeft, character: null, @@ -643,7 +643,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 3300); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 3300), - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -653,7 +653,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 3400); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 3400), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -672,7 +672,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a', kCtrl)..timeStamp = 200); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 200), - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -686,7 +686,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 800); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 800), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -699,7 +699,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2800), - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -709,7 +709,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2900), - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -725,7 +725,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -738,7 +738,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -747,7 +747,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -755,7 +755,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); expectKeyData(keyDataList.last, - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -770,7 +770,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, @@ -778,7 +778,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -790,21 +790,21 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')); expect(keyDataList, hasLength(3)); expectKeyData(keyDataList[0], - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, synthesized: true, ); expectKeyData(keyDataList[1], - change: ui.KeyChange.up, + change: ui.KeyEventType.up, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, synthesized: true, ); expectKeyData(keyDataList.last, - change: ui.KeyChange.down, + change: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -901,7 +901,7 @@ const kScrollLock = 0x4; void expectKeyData( ui.KeyData target, { - required ui.KeyChange change, + required ui.KeyEventType change, required int physical, required int logical, required String? character, diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index 408e53bf7a713..9be515458d956 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -40,7 +40,7 @@ void onPointerDataPacketMain() { PlatformDispatcher.instance.onPointerDataPacket = (PointerDataPacket packet) { List sequence = []; for (PointerData data in packet.data) { - sequence.add(PointerChange.values.indexOf(data.change)); + sequence.add(PointerChange.values.indexOf(data.type)); } nativeOnPointerDataPacket(sequence); }; diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index f2b0a09716baf..e5a789a224d15 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1492,16 +1492,16 @@ FlutterEngineResult FlutterEngineSendPointerEvent( "running Flutter application."); } -inline flutter::KeyChange ToKeyChange(FlutterKeyEventKind key_change) { +inline flutter::KeyEventType ToKeyEventType(FlutterKeyEventKind key_change) { switch (key_change) { case kFlutterKeyEventKindUp: - return flutter::KeyChange::kUp; + return flutter::KeyEventType::kUp; case kFlutterKeyEventKindDown: - return flutter::KeyChange::kDown; + return flutter::KeyEventType::kDown; case kFlutterKeyEventKindRepeat: - return flutter::KeyChange::kRepeat; + return flutter::KeyEventType::kRepeat; } - return flutter::KeyChange::kUp; + return flutter::KeyEventType::kUp; } // The number of bytes that should be able to fully store character data. @@ -1537,7 +1537,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( flutter::KeyData key_data; key_data.Clear(); key_data.timestamp = (uint64_t)SAFE_ACCESS(event, timestamp, 0); - key_data.change = ToKeyChange( + key_data.change = ToKeyEventType( SAFE_ACCESS(event, kind, FlutterKeyEventKind::kFlutterKeyEventKindUp)); key_data.physical = SAFE_ACCESS(event, physical, 0); key_data.logical = SAFE_ACCESS(event, logical, 0); diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index d6afa3b68dcb4..4fa2fb4dab6c1 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -504,13 +504,13 @@ void _echoKeyEvent( bool synthesized) native 'EchoKeyEvent'; -int _serializeKeyChange(KeyChange change) { +int _serializeKeyEventType(KeyEventType change) { switch(change) { - case KeyChange.up: + case KeyEventType.up: return 1; - case KeyChange.down: + case KeyEventType.down: return 2; - case KeyChange.repeat: + case KeyEventType.repeat: return 3; } } @@ -520,7 +520,7 @@ int _serializeKeyChange(KeyChange change) { void key_data_echo() async { // ignore: non_constant_identifier_names PlatformDispatcher.instance.onKeyData = (KeyData data) { _echoKeyEvent( - _serializeKeyChange(data.change), + _serializeKeyEventType(data.type), data.timeStamp.inMicroseconds, data.physical, data.logical, From 7de3628639165050c961bab43afdf5813d2e47ff Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 14:23:46 -0800 Subject: [PATCH 24/82] Rename callback --- lib/ui/key.dart | 27 +++++++++++----------- lib/ui/window/key_data.cc | 2 +- lib/ui/window/key_data.h | 6 ++--- lib/ui/window/key_data_packet.cc | 1 + lib/ui/window/key_data_packet.h | 3 --- lib/ui/window/platform_configuration.cc | 2 +- lib/ui/window/platform_configuration.h | 7 ++++-- lib/web_ui/lib/src/ui/key.dart | 20 ++++++++-------- runtime/runtime_controller.cc | 2 +- runtime/runtime_controller.h | 2 +- shell/common/engine.cc | 2 +- shell/common/engine.h | 2 +- shell/common/fixtures/shell_test.dart | 2 +- shell/common/platform_view.cc | 2 +- shell/common/platform_view.h | 6 +++-- shell/common/shell.cc | 2 +- shell/common/shell.h | 2 +- shell/common/shell_unittests.cc | 2 +- shell/platform/embedder/embedder.cc | 6 ++--- shell/platform/embedder/embedder_engine.cc | 2 +- shell/platform/embedder/embedder_engine.h | 2 +- 21 files changed, 53 insertions(+), 49 deletions(-) diff --git a/lib/ui/key.dart b/lib/ui/key.dart index 19641476bd1ed..e6a349d1a2c9d 100644 --- a/lib/ui/key.dart +++ b/lib/ui/key.dart @@ -6,7 +6,7 @@ part of dart.ui; -/// How the key has changed since the last report. +/// The type of a key event. // Must match the KeyEventType enum in ui/window/key_data.h. enum KeyEventType { /// The key is pressed. @@ -21,7 +21,7 @@ enum KeyEventType { /// Information about a key event. class KeyData { - /// Creates an object that represents the change of a key. + /// Creates an object that represents a key event. const KeyData({ required this.timeStamp, required this.type, @@ -60,17 +60,18 @@ class KeyData { /// /// For example, some key downs or ups might be lost when the window loses /// focus. Some platforms provides ways to query whether a key is being held. - /// If Flutter detects an inconsistancy between the state Flutter records and - /// the state returned by the system, Flutter will synthesize a corresponding - /// event to synchronize the state without breaking the event model. + /// If the embedder detects an inconsistancy between its internal record and + /// the state returned by the system, the embedder will synthesize a + /// corresponding event to synchronize the state without breaking the event + /// model. /// /// As another example, macOS treats CapsLock in a special way by sending /// down and up events at the down of alterate presses to indicate the /// direction in which the lock is toggled instead of that the physical key is - /// going. Flutter normalizes the behavior by converting a native down event - /// into a down event followed immediately by a synthesized up event, and - /// the native up event also into a down event followed immediately by a - /// synthesized up event. + /// going. A macOS embedder should normalize the behavior by converting a + /// native down event into a down event followed immediately by a synthesized + /// up event, and the native up event also into a down event followed + /// immediately by a synthesized up event. /// /// Synthesized events do not have a trustworthy [timeStamp], and should not be /// processed as if the key actually went down or up at the time of the @@ -80,13 +81,13 @@ class KeyData { final bool synthesized; @override - String toString() => 'KeyData(change: ${_changeToString(type)}, physical: 0x${physical.toRadixString(16)}, ' + String toString() => 'KeyData(type: ${_typeToString(type)}, physical: 0x${physical.toRadixString(16)}, ' 'logical: 0x${logical.toRadixString(16)}, character: $character)'; /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(' - 'change: ${_changeToString(type)}, ' + 'type: ${_typeToString(type)}, ' 'timeStamp: $timeStamp, ' 'physical: 0x${physical.toRadixString(16)}, ' 'logical: 0x${logical.toRadixString(16)}, ' @@ -95,8 +96,8 @@ class KeyData { ')'; } - static String _changeToString(KeyEventType change) { - switch (change) { + static String _typeToString(KeyEventType type) { + switch (type) { case KeyEventType.up: return 'up'; case KeyEventType.down: diff --git a/lib/ui/window/key_data.cc b/lib/ui/window/key_data.cc index 875f89f7522c2..2dacd8d7bea43 100644 --- a/lib/ui/window/key_data.cc +++ b/lib/ui/window/key_data.cc @@ -4,7 +4,7 @@ #include "flutter/lib/ui/window/key_data.h" -#include +#include namespace flutter { diff --git a/lib/ui/window/key_data.h b/lib/ui/window/key_data.h index 1dd1892acab6c..a9220a7c225cb 100644 --- a/lib/ui/window/key_data.h +++ b/lib/ui/window/key_data.h @@ -5,7 +5,7 @@ #ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_H_ #define FLUTTER_LIB_UI_WINDOW_KEY_DATA_H_ -#include +#include namespace flutter { @@ -31,7 +31,7 @@ enum class KeyEventType : int64_t { struct alignas(8) KeyData { // Timestamp in microseconds from an arbitrary and consistant start point uint64_t timestamp; - KeyEventType change; + KeyEventType type; uint64_t physical; uint64_t logical; // True if the event does not correspond to a native event. @@ -39,7 +39,7 @@ struct alignas(8) KeyData { // The value is 1 for true, and 0 for false. uint64_t synthesized; - // Set all contents of `Keydata` to 0. + // Sets all contents of `Keydata` to 0. void Clear(); }; diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc index a476be497a3e7..2b67c43fd303c 100644 --- a/lib/ui/window/key_data_packet.cc +++ b/lib/ui/window/key_data_packet.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/lib/ui/window/key_data_packet.h" + #include "flutter/fml/logging.h" namespace flutter { diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h index 735bebd1d2cc1..aae2507e0a2a6 100644 --- a/lib/ui/window/key_data_packet.h +++ b/lib/ui/window/key_data_packet.h @@ -5,7 +5,6 @@ #ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_MESSAGE_H_ #define FLUTTER_LIB_UI_WINDOW_KEY_DATA_MESSAGE_H_ -#include #include #include @@ -14,8 +13,6 @@ namespace flutter { -typedef std::function KeyDataPacketCallback; - class KeyDataPacket { public: // Build a KeyDataPacket by incrementally fill in data. diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 0eee18ce4a8f5..70219eb9a687d 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -360,7 +360,7 @@ void PlatformConfiguration::DispatchSemanticsAction(int32_t id, } uint64_t PlatformConfiguration::RegisterKeyDataResponse( - KeyDataPacketCallback callback) { + KeyDataResponse callback) { uint64_t response_id = next_key_response_id_++; pending_key_responses_[response_id] = std::move(callback); return response_id; diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 8a9f30f9c04d5..0a30c7620ba02 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_LIB_UI_WINDOW_PLATFORM_CONFIGURATION_H_ #define FLUTTER_LIB_UI_WINDOW_PLATFORM_CONFIGURATION_H_ +#include #include #include #include @@ -22,6 +23,8 @@ class FontCollection; class PlatformMessage; class Scene; +typedef std::function KeyDataResponse; + //-------------------------------------------------------------------------- /// @brief An enum for defining the different kinds of accessibility features /// that can be enabled by the platform. @@ -339,7 +342,7 @@ class PlatformConfiguration final { /// @return The response ID to be associated with the callback. Using this /// ID in CompleteKeyDataResponse will invoke the callback. /// - uint64_t RegisterKeyDataResponse(KeyDataPacketCallback callback); + uint64_t RegisterKeyDataResponse(KeyDataResponse callback); //---------------------------------------------------------------------------- /// @brief Notifies the framework that it is time to begin working on a @@ -467,7 +470,7 @@ class PlatformConfiguration final { // We use id 0 to mean that no response is expected. uint64_t next_key_response_id_ = 1; - std::unordered_map + std::unordered_map pending_key_responses_; }; diff --git a/lib/web_ui/lib/src/ui/key.dart b/lib/web_ui/lib/src/ui/key.dart index 9ae86116ca6a2..14fb39393f904 100644 --- a/lib/web_ui/lib/src/ui/key.dart +++ b/lib/web_ui/lib/src/ui/key.dart @@ -6,7 +6,7 @@ part of ui; -/// How the key has changed since the last report. +/// The type of a key event. // Must match the KeyEventType enum in ui/window/key_data.h. enum KeyEventType { /// The key is pressed. @@ -19,12 +19,12 @@ enum KeyEventType { repeat, } -/// Information about the change of a key. +/// Information about a key event. class KeyData { - /// Creates an object that represents the change of a key. + /// Creates an object that represents a key event. const KeyData({ required this.timeStamp, - required this.change, + required this.type, required this.physical, required this.logical, required this.character, @@ -37,8 +37,8 @@ class KeyData { /// might not be the actual time that the key press or release happens. final Duration timeStamp; - /// How the key has changed since the last report. - final KeyEventType change; + /// The type of the event. + final KeyEventType type; /// The key code for the physical key that has changed. final int physical; @@ -80,13 +80,13 @@ class KeyData { final bool synthesized; @override - String toString() => 'KeyData(change: ${_changeToString(change)}, physical: 0x${physical.toRadixString(16)}, ' + String toString() => 'KeyData(type: ${_typeToString(type)}, physical: 0x${physical.toRadixString(16)}, ' 'logical: 0x${logical.toRadixString(16)}, character: $character)'; /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(' - 'change: ${_changeToString(change)}, ' + 'type: ${_typeToString(type)}, ' 'timeStamp: $timeStamp, ' 'physical: 0x${physical.toRadixString(16)}, ' 'logical: 0x${logical.toRadixString(16)}, ' @@ -95,8 +95,8 @@ class KeyData { ')'; } - static String _changeToString(KeyEventType change) { - switch (change) { + static String _typeToString(KeyEventType type) { + switch (type) { case KeyEventType.up: return 'up'; case KeyEventType.down: diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 91d214e25ea37..45c7050abf690 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -264,7 +264,7 @@ bool RuntimeController::DispatchPointerDataPacket( bool RuntimeController::DispatchKeyDataPacket( const KeyDataPacket& packet, - KeyDataPacketCallback callback) { + KeyDataResponse callback) { if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataPacket", "mode", "basic"); diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 1057eb35bb682..6793b1a32f3b1 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -436,7 +436,7 @@ class RuntimeController : public PlatformConfigurationClient { /// an isolate is not running. /// bool DispatchKeyDataPacket(const KeyDataPacket& packet, - KeyDataPacketCallback callback); + KeyDataResponse callback); //---------------------------------------------------------------------------- /// @brief Dispatch the semantics action to the specified accessibility diff --git a/shell/common/engine.cc b/shell/common/engine.cc index e44fef37f9af7..7fa1dbe38a440 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -417,7 +417,7 @@ void Engine::DispatchPointerDataPacket( void Engine::DispatchKeyDataPacket( std::unique_ptr packet, - KeyDataPacketCallback callback) { + KeyDataResponse callback) { TRACE_EVENT0("flutter", "Engine::DispatchKeyDataPacket"); if (runtime_controller_) { runtime_controller_->DispatchKeyDataPacket(*packet, std::move(callback)); diff --git a/shell/common/engine.h b/shell/common/engine.h index ae86e330f5d29..43d56b822eda4 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -740,7 +740,7 @@ class Engine final : public RuntimeDelegate, /// to handle this key data. /// void DispatchKeyDataPacket(std::unique_ptr packet, - KeyDataPacketCallback callback); + KeyDataResponse callback); //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder encountered an diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index 9be515458d956..408e53bf7a713 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -40,7 +40,7 @@ void onPointerDataPacketMain() { PlatformDispatcher.instance.onPointerDataPacket = (PointerDataPacket packet) { List sequence = []; for (PointerData data in packet.data) { - sequence.add(PointerChange.values.indexOf(data.type)); + sequence.add(PointerChange.values.indexOf(data.change)); } nativeOnPointerDataPacket(sequence); }; diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index e83e10dd42a03..53a3726aaf0ca 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -44,7 +44,7 @@ void PlatformView::DispatchPointerDataPacket( void PlatformView::DispatchKeyDataPacket( std::unique_ptr packet, - KeyDataPacketCallback callback) { + KeyDataResponse callback) { delegate_.OnPlatformViewDispatchKeyDataPacket(std::move(packet), std::move(callback)); } diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index eeba64c94f65f..ac8e7338dd925 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -6,6 +6,7 @@ #define COMMON_PLATFORM_VIEW_H_ #include +#include #include "flow/embedded_views.h" #include "flutter/common/graphics/texture.h" @@ -53,6 +54,7 @@ class PlatformView { /// class Delegate { public: + using KeyDataResponse = std::function; //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view was created /// with the given render surface. This surface is platform @@ -138,7 +140,7 @@ class PlatformView { /// virtual void OnPlatformViewDispatchKeyDataPacket( std::unique_ptr packet, - KeyDataPacketCallback callback) = 0; + std::function callback) = 0; //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view has encountered @@ -599,7 +601,7 @@ class PlatformView { /// @param[in] packet The key data packet to dispatch to the framework. /// void DispatchKeyDataPacket(std::unique_ptr packet, - KeyDataPacketCallback callback); + Delegate::KeyDataResponse callback); //-------------------------------------------------------------------------- /// @brief Used by the embedder to specify a texture that it wants the diff --git a/shell/common/shell.cc b/shell/common/shell.cc index b69feca3e1ff4..d1adb4eedc1a7 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -964,7 +964,7 @@ void Shell::OnPlatformViewDispatchPointerDataPacket( // |PlatformView::Delegate| void Shell::OnPlatformViewDispatchKeyDataPacket( std::unique_ptr packet, - KeyDataPacketCallback callback) { + std::function callback) { TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataPacket"); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); diff --git a/shell/common/shell.h b/shell/common/shell.h index c221c5d89282b..716f5c72d3aaf 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -507,7 +507,7 @@ class Shell final : public PlatformView::Delegate, // |PlatformView::Delegate| void OnPlatformViewDispatchKeyDataPacket( std::unique_ptr packet, - KeyDataPacketCallback callback) override; + std::function callback) override; // |PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction( diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index a7d19dc749aa2..0b9a98f211567 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -64,7 +64,7 @@ class MockPlatformViewDelegate : public PlatformView::Delegate { MOCK_METHOD2(OnPlatformViewDispatchKeyDataPacket, void(std::unique_ptr packet, - KeyDataPacketCallback callback)); + KeyDataResponse callback)); MOCK_METHOD3(OnPlatformViewDispatchSemanticsAction, void(int32_t id, diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index e5a789a224d15..3aa3d9d182d48 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1492,8 +1492,8 @@ FlutterEngineResult FlutterEngineSendPointerEvent( "running Flutter application."); } -inline flutter::KeyEventType ToKeyEventType(FlutterKeyEventKind key_change) { - switch (key_change) { +inline flutter::KeyEventType ToKeyEventType(FlutterKeyEventKind event_kind) { + switch (event_kind) { case kFlutterKeyEventKindUp: return flutter::KeyEventType::kUp; case kFlutterKeyEventKindDown: @@ -1537,7 +1537,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( flutter::KeyData key_data; key_data.Clear(); key_data.timestamp = (uint64_t)SAFE_ACCESS(event, timestamp, 0); - key_data.change = ToKeyEventType( + key_data.type = ToKeyEventType( SAFE_ACCESS(event, kind, FlutterKeyEventKind::kFlutterKeyEventKindUp)); key_data.physical = SAFE_ACCESS(event, physical, 0); key_data.logical = SAFE_ACCESS(event, logical, 0); diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index 4c14cd9a42a3e..c1e40cc41b04d 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -138,7 +138,7 @@ bool EmbedderEngine::DispatchPointerDataPacket( bool EmbedderEngine::DispatchKeyDataPacket( std::unique_ptr packet, - KeyDataPacketCallback callback) { + KeyDataResponse callback) { if (!IsValid() || !packet) { return false; } diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index b25f76e9f5466..a06d74e383472 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -61,7 +61,7 @@ class EmbedderEngine { std::unique_ptr packet); bool DispatchKeyDataPacket(std::unique_ptr packet, - KeyDataPacketCallback callback); + KeyDataResponse callback); bool SendPlatformMessage(fml::RefPtr message); From 86c4e9b5a3f64ac26a5c9b1e85b221876bc43b00 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 14:52:07 -0800 Subject: [PATCH 25/82] Simplify KeyDataPacket's API. --- lib/ui/platform_dispatcher.dart | 5 +---- lib/ui/window/key_data_packet.cc | 20 ++++++++------------ lib/ui/window/key_data_packet.h | 18 +++++++++--------- shell/platform/embedder/embedder.cc | 10 +++------- 4 files changed, 21 insertions(+), 32 deletions(-) diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 703486093cee5..612de46431bb0 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -374,10 +374,7 @@ class PlatformDispatcher { // * HardwareKeyboard.java static const int _kKeyDataFieldCount = 5; - // KeyData packet structure: - // | CharDataSize | (1 field) - // | Key Data | (_kKeyDataFieldCount fields) - // | CharData | (CharDataSize bits) + // The packet structure is described in `key_data_packet.h`. static KeyData _unpackKeyData(ByteData packet) { const int kStride = Int64List.bytesPerElement; diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc index 2b67c43fd303c..ca54e23ad5517 100644 --- a/lib/ui/window/key_data_packet.cc +++ b/lib/ui/window/key_data_packet.cc @@ -4,27 +4,23 @@ #include "flutter/lib/ui/window/key_data_packet.h" +#include + #include "flutter/fml/logging.h" namespace flutter { -KeyDataPacket::KeyDataPacket(size_t character_data_size) - : data_(sizeof(uint64_t) + sizeof(KeyData) + character_data_size) { - uint64_t size64 = character_data_size; - memcpy(&data_[CharacterSizeStart_()], &size64, sizeof(size64)); -} - -KeyDataPacket::~KeyDataPacket() = default; - -void KeyDataPacket::SetKeyData(const KeyData& event) { +KeyDataPacket::KeyDataPacket(const KeyData& event, const char* character) { + uint64_t char_size = character == nullptr ? 0 : strlen(character); + data_.resize(sizeof(uint64_t) + sizeof(KeyData) + char_size); + memcpy(&data_[CharacterSizeStart_()], &char_size, sizeof(char_size)); memcpy(&data_[KeyDataStart_()], &event, sizeof(KeyData)); -} - -void KeyDataPacket::SetCharacter(const char* character) { if (character != nullptr) { memcpy(data_.data() + CharacterStart_(), character, data_.size() - CharacterStart_()); } } +KeyDataPacket::~KeyDataPacket() = default; + } // namespace flutter diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h index aae2507e0a2a6..2d80a17ef4fb8 100644 --- a/lib/ui/window/key_data_packet.h +++ b/lib/ui/window/key_data_packet.h @@ -13,23 +13,23 @@ namespace flutter { +// A byte stream representing a key event, to be sent to the framework. class KeyDataPacket { public: - // Build a KeyDataPacket by incrementally fill in data. + // Build the key data packet by providing information. // - // The `character_data_size` is number of bytes to contain the character data. - KeyDataPacket(size_t character_data_size); + // The `character` is a nullable C-string that ends with a '\0'. + KeyDataPacket(const KeyData& event, const char* character); ~KeyDataPacket(); const std::vector& data() const { return data_; } - void SetKeyData(const KeyData& event); - - // Set character data to the proper position, which should not be terminated - // by a null character (length controled by character_data_size). - void SetCharacter(const char* characters); - private: + // Packet structure: + // | CharDataSize | (1 field) + // | Key Data | (kKeyDataFieldCount fields) + // | CharData | (CharDataSize bits) + size_t CharacterSizeStart_() { return 0; } size_t KeyDataStart_() { return CharacterSizeStart_() + sizeof(uint64_t); } size_t CharacterStart_() { return KeyDataStart_() + sizeof(KeyData); } diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 3aa3d9d182d48..b87acebe37ad6 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1529,11 +1529,6 @@ FlutterEngineResult FlutterEngineSendKeyEvent( const char* character = SAFE_ACCESS(event, character, nullptr); - size_t character_data_size = - character == nullptr ? 0 : strnlen(character, kKeyEventCharacterMaxBytes); - - auto packet = std::make_unique(character_data_size); - flutter::KeyData key_data; key_data.Clear(); key_data.timestamp = (uint64_t)SAFE_ACCESS(event, timestamp, 0); @@ -1542,8 +1537,9 @@ FlutterEngineResult FlutterEngineSendKeyEvent( key_data.physical = SAFE_ACCESS(event, physical, 0); key_data.logical = SAFE_ACCESS(event, logical, 0); key_data.synthesized = SAFE_ACCESS(event, synthesized, false); - packet->SetKeyData(key_data); - packet->SetCharacter(character); + + auto packet = std::make_unique( + key_data, character); auto response = [callback, user_data](bool handled) { callback(handled, user_data); From b367ce3c97c016a5aadadbfd789a6e5537ffc292 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 15:34:34 -0800 Subject: [PATCH 26/82] More comments --- lib/ui/window/key_data_packet.cc | 10 +++++----- lib/ui/window/key_data_packet.h | 12 +++++++----- lib/ui/window/platform_configuration.cc | 4 ++-- lib/ui/window/platform_configuration.h | 6 +++--- lib/ui/window/window.h | 11 ++++++++++- lib/web_ui/lib/src/engine/platform_dispatcher.dart | 2 +- shell/platform/embedder/embedder.cc | 7 ++++--- shell/platform/embedder/embedder.h | 4 ++-- 8 files changed, 34 insertions(+), 22 deletions(-) diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc index ca54e23ad5517..fe24af210cd67 100644 --- a/lib/ui/window/key_data_packet.cc +++ b/lib/ui/window/key_data_packet.cc @@ -11,13 +11,13 @@ namespace flutter { KeyDataPacket::KeyDataPacket(const KeyData& event, const char* character) { - uint64_t char_size = character == nullptr ? 0 : strlen(character); + size_t char_size = character == nullptr ? 0 : strlen(character); + uint64_t char_size_64 = char_size; data_.resize(sizeof(uint64_t) + sizeof(KeyData) + char_size); - memcpy(&data_[CharacterSizeStart_()], &char_size, sizeof(char_size)); - memcpy(&data_[KeyDataStart_()], &event, sizeof(KeyData)); + memcpy(CharacterSizeStart(), &char_size_64, sizeof(char_size)); + memcpy(KeyDataStart(), &event, sizeof(KeyData)); if (character != nullptr) { - memcpy(data_.data() + CharacterStart_(), character, - data_.size() - CharacterStart_()); + memcpy(CharacterStart(), character, char_size); } } diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h index 2d80a17ef4fb8..f340a891d0a29 100644 --- a/lib/ui/window/key_data_packet.h +++ b/lib/ui/window/key_data_packet.h @@ -22,6 +22,10 @@ class KeyDataPacket { KeyDataPacket(const KeyData& event, const char* character); ~KeyDataPacket(); + // Prevent copying. + KeyDataPacket(KeyDataPacket const&) = delete; + KeyDataPacket& operator=(KeyDataPacket const&) = delete; + const std::vector& data() const { return data_; } private: @@ -30,13 +34,11 @@ class KeyDataPacket { // | Key Data | (kKeyDataFieldCount fields) // | CharData | (CharDataSize bits) - size_t CharacterSizeStart_() { return 0; } - size_t KeyDataStart_() { return CharacterSizeStart_() + sizeof(uint64_t); } - size_t CharacterStart_() { return KeyDataStart_() + sizeof(KeyData); } + uint8_t* CharacterSizeStart() { return data_.data(); } + uint8_t* KeyDataStart() { return CharacterSizeStart() + sizeof(uint64_t); } + uint8_t* CharacterStart() { return KeyDataStart() + sizeof(KeyData); } std::vector data_; - - FML_DISALLOW_COPY_AND_ASSIGN(KeyDataPacket); }; } // namespace flutter diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 70219eb9a687d..024041e820699 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -443,7 +443,7 @@ void PlatformConfiguration::CompletePlatformMessageResponse( } void PlatformConfiguration::CompleteKeyDataResponse(uint64_t response_id, bool handled) { - if (!response_id) { + if (response_id == 0) { return; } auto it = pending_key_responses_.find(response_id); @@ -451,7 +451,7 @@ void PlatformConfiguration::CompleteKeyDataResponse(uint64_t response_id, bool h if (it == pending_key_responses_.end()) { return; } - auto callback = std::move(it->second); + KeyDataResponse callback = std::move(it->second); pending_key_responses_.erase(it); callback(handled); } diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 0a30c7620ba02..2d8a800bd8b76 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -327,7 +327,7 @@ class PlatformConfiguration final { std::vector args); //---------------------------------------------------------------------------- - /// @brief Register a callback to be invoked when the framework has + /// @brief Registers a callback to be invoked when the framework has /// decided whether to handle an event. This callback originates /// in the platform view and has been forwarded through the engine /// to here. @@ -463,12 +463,12 @@ class PlatformConfiguration final { std::unordered_map> windows_; - // We use id 0 to mean that no response is expected. + // ID starts at 1 because an ID of 0 indicates that no response is expected. int next_response_id_ = 1; std::unordered_map> pending_responses_; - // We use id 0 to mean that no response is expected. + // ID starts at 1 because an ID of 0 indicates that no response is expected. uint64_t next_key_response_id_ = 1; std::unordered_map pending_key_responses_; diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index 051caf0c76ccb..aae68604ffdeb 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -5,10 +5,10 @@ #ifndef FLUTTER_LIB_UI_WINDOW_WINDOW_H_ #define FLUTTER_LIB_UI_WINDOW_WINDOW_H_ +#include #include #include #include -#include #include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/lib/ui/window/platform_message.h" @@ -28,7 +28,16 @@ class Window final { const ViewportMetrics& viewport_metrics() const { return viewport_metrics_; } + // Dispatch a packet to the framework that indicates one or a few pointer + // events. void DispatchPointerDataPacket(const PointerDataPacket& packet); + // Dispatch a packet to the framework that indicates a key event. + // + // The `response_id` is used to label the response of whether the key event + // is handled by the framework, typically the return value of + // PlatformConfiguration::RegisterKeyDataResponse. + // It should be used later in + // PlatformConfiguration::CompleteKeyDataResponse. void DispatchKeyDataPacket(const KeyDataPacket& packet, uint64_t response_id); void UpdateWindowMetrics(const ViewportMetrics& metrics); diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index c719e8fbbc18a..10faac0b9af5f 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -172,7 +172,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { invoke1(_onPointerDataPacket, _onPointerDataPacketZone, dataPacket); } - /// A callback that is invoked when pointer data is available. + /// A callback that is invoked when key data is available. /// /// The framework invokes this callback in the same zone in which the /// callback was set. diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index b87acebe37ad6..7120da5947b5f 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1512,7 +1512,7 @@ inline flutter::KeyEventType ToKeyEventType(FlutterKeyEventKind event_kind) { // Many platforms assert the character to be less than 2 int16's, i.e. 4 bytes, // therefore the character data is asserted to be less than double the amount, // i.e. 8 bytes. -constexpr size_t kKeyEventCharacterMaxBytes = 8; +static constexpr size_t kKeyEventCharacterMaxBytes = 8; FlutterEngineResult FlutterEngineSendKeyEvent( FLUTTER_API_SYMBOL(FlutterEngine) engine, @@ -1542,8 +1542,9 @@ FlutterEngineResult FlutterEngineSendKeyEvent( key_data, character); auto response = [callback, user_data](bool handled) { - callback(handled, user_data); - }; + if (callback != nullptr) + callback(handled, user_data); + }; return reinterpret_cast(engine) ->DispatchKeyDataPacket( diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index a0f4588552460..79572a6cb1e4a 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -617,7 +617,7 @@ typedef enum { kFlutterKeyEventKindRepeat, } FlutterKeyEventKind; -/// A structure to represent a change of state of a key. +/// A structure to represent a key event. /// /// Sending `FlutterKeyEvent` via `FlutterEngineSendKeyEvent` results in a /// corresponding `FlutterKeyEvent` to be dispatched in the framework. It is @@ -1621,7 +1621,7 @@ FlutterEngineResult FlutterEngineSendPointerEvent( /// longer access `event` after returning. /// @param[in] callback The callback invoked by the engine when the /// Flutter application has decided whether it handles -/// this event. +/// this event. This can be null. /// @param[in] user_data The context associated with the callback. The exact /// same value will used to invoke `callback`. Accepts /// nullptr or a non-pointer value. From 855ffdeb012f0396a3d69a05516b3dab4511c26f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 15:37:29 -0800 Subject: [PATCH 27/82] Rename embedder kind -> type --- shell/platform/embedder/embedder.cc | 12 +++++----- shell/platform/embedder/embedder.h | 12 +++++----- .../embedder/tests/embedder_unittests.cc | 22 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 7120da5947b5f..a3d3189b04fae 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1492,13 +1492,13 @@ FlutterEngineResult FlutterEngineSendPointerEvent( "running Flutter application."); } -inline flutter::KeyEventType ToKeyEventType(FlutterKeyEventKind event_kind) { +static inline flutter::KeyEventType MapKeyEventType(FlutterKeyEventType event_kind) { switch (event_kind) { - case kFlutterKeyEventKindUp: + case kFlutterKeyEventTypeUp: return flutter::KeyEventType::kUp; - case kFlutterKeyEventKindDown: + case kFlutterKeyEventTypeDown: return flutter::KeyEventType::kDown; - case kFlutterKeyEventKindRepeat: + case kFlutterKeyEventTypeRepeat: return flutter::KeyEventType::kRepeat; } return flutter::KeyEventType::kUp; @@ -1532,8 +1532,8 @@ FlutterEngineResult FlutterEngineSendKeyEvent( flutter::KeyData key_data; key_data.Clear(); key_data.timestamp = (uint64_t)SAFE_ACCESS(event, timestamp, 0); - key_data.type = ToKeyEventType( - SAFE_ACCESS(event, kind, FlutterKeyEventKind::kFlutterKeyEventKindUp)); + key_data.type = MapKeyEventType( + SAFE_ACCESS(event, type, FlutterKeyEventType::kFlutterKeyEventTypeUp)); key_data.physical = SAFE_ACCESS(event, physical, 0); key_data.logical = SAFE_ACCESS(event, logical, 0); key_data.synthesized = SAFE_ACCESS(event, synthesized, false); diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 79572a6cb1e4a..1566a88a9730f 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -612,10 +612,10 @@ typedef struct { } FlutterPointerEvent; typedef enum { - kFlutterKeyEventKindUp = 1, - kFlutterKeyEventKindDown, - kFlutterKeyEventKindRepeat, -} FlutterKeyEventKind; + kFlutterKeyEventTypeUp = 1, + kFlutterKeyEventTypeDown, + kFlutterKeyEventTypeRepeat, +} FlutterKeyEventType; /// A structure to represent a key event. /// @@ -626,7 +626,7 @@ typedef enum { /// the following rules: /// /// * Each key press sequence shall consist of one key down event (`kind` being -/// `kFlutterKeyEventKindDown`), zero or more repeat events, and one key up +/// `kFlutterKeyEventTypeDown`), zero or more repeat events, and one key up /// event, representing a physical key button being pressed, held, and released. /// * All events throughout a key press sequence shall have the same `physical` /// and `logical`. Having different `character`s is allowed. @@ -638,7 +638,7 @@ typedef struct { /// that used by `FlutterEngineGetCurrentTime`. double timestamp; /// The event kind. - FlutterKeyEventKind kind; + FlutterKeyEventType type; /// The USB HID code for the physical key of the event. /// /// For the full definition and list of pre-defined physical keys, see diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 25763cf1af7af..914ce86763fb7 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1200,23 +1200,23 @@ typedef struct { bool returned; } KeyEventUserData; -FlutterKeyEventKind unserializeKeyEventKind(uint64_t kindInt) { +FlutterKeyEventType unserializeKeyEventKind(uint64_t kindInt) { switch(kindInt) { case 1: - return kFlutterKeyEventKindUp; + return kFlutterKeyEventTypeUp; case 2: - return kFlutterKeyEventKindDown; + return kFlutterKeyEventTypeDown; case 3: - return kFlutterKeyEventKindRepeat; + return kFlutterKeyEventTypeRepeat; default: FML_UNREACHABLE(); - return kFlutterKeyEventKindUp; + return kFlutterKeyEventTypeUp; } } void expect_key_event_eq(const FlutterKeyEvent& subject, const FlutterKeyEvent& baseline) { EXPECT_EQ(subject.timestamp, baseline.timestamp); - EXPECT_EQ(subject.kind, baseline.kind); + EXPECT_EQ(subject.type, baseline.type); EXPECT_EQ(subject.physical, baseline.physical); EXPECT_EQ(subject.logical, baseline.logical); EXPECT_EQ(subject.synthesized, baseline.synthesized); @@ -1228,7 +1228,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { FlutterKeyEvent echoed_event; auto native_echo_event = [&](Dart_NativeArguments args) { - echoed_event.kind = unserializeKeyEventKind( + echoed_event.type = unserializeKeyEventKind( tonic::DartConverter::FromDart( Dart_GetNativeArgument(args, 0))); echoed_event.timestamp = tonic::DartConverter::FromDart( @@ -1265,7 +1265,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { const FlutterKeyEvent downEventUpperA { .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1, - .kind = kFlutterKeyEventKindDown, + .type = kFlutterKeyEventTypeDown, .physical = 0x00070004, .logical = 0x00000000061, .character = "A", @@ -1282,7 +1282,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { const FlutterKeyEvent repeatEventWideChar { .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000, - .kind = kFlutterKeyEventKindRepeat, + .type = kFlutterKeyEventTypeRepeat, .physical = 0x00070005, .logical = 0x00000000062, .character = "∆", @@ -1299,7 +1299,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { const FlutterKeyEvent upEvent { .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000000, - .kind = kFlutterKeyEventKindUp, + .type = kFlutterKeyEventTypeUp, .physical = 0x00070006, .logical = 0x00000000063, .character = nullptr, @@ -1335,7 +1335,7 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { FlutterKeyEvent event { .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000, - .kind = kFlutterKeyEventKindDown, + .type = kFlutterKeyEventTypeDown, .physical = 0x00070005, .logical = 0x00000000062, .character = nullptr, From 8cbf7e190aa666bf4f805531faea300b35be9126 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 16:28:38 -0800 Subject: [PATCH 28/82] All comments --- shell/common/engine.h | 9 +++--- shell/platform/embedder/embedder.h | 21 ++++++------ shell/platform/embedder/embedder_engine.h | 12 +++++++ shell/platform/embedder/fixtures/main.dart | 4 +++ .../embedder/tests/embedder_unittests.cc | 32 +++++++++++-------- 5 files changed, 50 insertions(+), 28 deletions(-) diff --git a/shell/common/engine.h b/shell/common/engine.h index 43d56b822eda4..4249ceb2eba01 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -730,10 +730,11 @@ class Engine final : public RuntimeDelegate, //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder has sent it a key data - /// packet. A key data packet contains one physical key event and - /// one or multiple logical key events. This call originates in - /// the platform view and the shell has forwarded the same to the - /// engine on the UI task runner here. + /// packet. A key data packet contains one key event. This call + /// originates in the platform view and the shell has forwarded + /// the same to the engine on the UI task runner here. The engine + /// will decide whether to handle this event, and send the + /// result using `callback`, which will be called exactly once. /// /// @param[in] packet The key data packet. /// @param[in] callback Called when the framework has decided whether diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 1566a88a9730f..428d7d08ff9f3 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -633,9 +633,9 @@ typedef enum { typedef struct { /// The size of this struct. Must be sizeof(FlutterKeyEvent). size_t struct_size; - /// The timestamp at which the pointer event was generated. The timestamp - /// should be specified in microseconds and the clock should be the same as - /// that used by `FlutterEngineGetCurrentTime`. + /// The timestamp at which the key event was generated. The timestamp should be + /// specified in microseconds and the clock should be the same as that used by + /// `FlutterEngineGetCurrentTime`. double timestamp; /// The event kind. FlutterKeyEventType type; @@ -1609,22 +1609,21 @@ FlutterEngineResult FlutterEngineSendPointerEvent( size_t events_count); //------------------------------------------------------------------------------ -/// @brief Send a key event to the engine, causing the framework to -/// dispatch a key event that is simply transformed from this one. -/// The framework will decide whether to handle this event in a -/// synchronous fashion, although due to technical limitation, the -/// result is always reported asynchronously. The `callback` is -/// guaranteed to be called exactly once. +/// @brief Sends a key event to the engine. The framework will decide +/// whether to handle this event in a synchronous fashion, although +/// due to technical limitation, the result is always reported +/// asynchronously. The `callback` is guaranteed to be called +/// exactly once. /// /// @param[in] engine A running engine instance. /// @param[in] event The event data to be sent. This function will no /// longer access `event` after returning. /// @param[in] callback The callback invoked by the engine when the /// Flutter application has decided whether it handles -/// this event. This can be null. +/// this event. Accepts nullptr. /// @param[in] user_data The context associated with the callback. The exact /// same value will used to invoke `callback`. Accepts -/// nullptr or a non-pointer value. +/// nullptr. /// /// @return The result of the call. /// diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index a06d74e383472..bdff752a9eceb 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -60,6 +60,18 @@ class EmbedderEngine { bool DispatchPointerDataPacket( std::unique_ptr packet); + //---------------------------------------------------------------------------- + /// @brief Notifies the platform view that the embedder has sent it a key + /// data packet. A key data packet contains one key event. This + /// call originates in the platform view and the shell has forwarded + /// the same to the engine on the UI task runner here. The platform + /// view will decide whether to handle this event, and send the + /// result using `callback`, which will be called exactly once. + /// + /// @param[in] packet The key data packet. + /// @param[in] callback Called when the framework has decided whether + /// to handle this key data. + /// bool DispatchKeyDataPacket(std::unique_ptr packet, KeyDataResponse callback); diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 4fa2fb4dab6c1..9d0458af6fdcd 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -504,6 +504,10 @@ void _echoKeyEvent( bool synthesized) native 'EchoKeyEvent'; +// Convert `kind` in enum form to its integer form. +// +// It performs a revesed mapping from `unserializeKeyEventKind` +// in shell/platform/embedder/tests/embedder_unittests.cc. int _serializeKeyEventType(KeyEventType change) { switch(change) { case KeyEventType.up: diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 914ce86763fb7..faad0634141fd 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1200,8 +1200,12 @@ typedef struct { bool returned; } KeyEventUserData; -FlutterKeyEventType unserializeKeyEventKind(uint64_t kindInt) { - switch(kindInt) { +// Convert `kind` in integer form to its enum form. +// +// It performs a revesed mapping from `_serializeKeyEventType` +// in shell/platform/embedder/fixtures/main.dart. +FlutterKeyEventType UnserializeKeyEventKind(uint64_t kind) { + switch(kind) { case 1: return kFlutterKeyEventTypeUp; case 2: @@ -1214,7 +1218,9 @@ FlutterKeyEventType unserializeKeyEventKind(uint64_t kindInt) { } } -void expect_key_event_eq(const FlutterKeyEvent& subject, const FlutterKeyEvent& baseline) { +// Checks the equality of two `FlutterKeyEvent` by each of their members except +// for `character`. The `character` must be checked separately. +void ExpectKeyEventEq(const FlutterKeyEvent& subject, const FlutterKeyEvent& baseline) { EXPECT_EQ(subject.timestamp, baseline.timestamp); EXPECT_EQ(subject.type, baseline.type); EXPECT_EQ(subject.physical, baseline.physical); @@ -1228,7 +1234,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { FlutterKeyEvent echoed_event; auto native_echo_event = [&](Dart_NativeArguments args) { - echoed_event.type = unserializeKeyEventKind( + echoed_event.type = UnserializeKeyEventKind( tonic::DartConverter::FromDart( Dart_GetNativeArgument(args, 0))); echoed_event.timestamp = tonic::DartConverter::FromDart( @@ -1262,7 +1268,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { ready.Wait(); // A normal down event - const FlutterKeyEvent downEventUpperA { + const FlutterKeyEvent down_event_upper_a { .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1, .type = kFlutterKeyEventTypeDown, @@ -1271,15 +1277,15 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { .character = "A", .synthesized = false, }; - FlutterEngineSendKeyEvent(engine.get(), &downEventUpperA, + FlutterEngineSendKeyEvent(engine.get(), &down_event_upper_a, [](bool handled, void* user_data){}, nullptr); message_latch->Wait(); - expect_key_event_eq(echoed_event, downEventUpperA); + ExpectKeyEventEq(echoed_event, down_event_upper_a); EXPECT_EQ(echoed_char, 0x41llu); // A repeat event with multi-byte character - const FlutterKeyEvent repeatEventWideChar { + const FlutterKeyEvent repeat_event_wide_char { .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000, .type = kFlutterKeyEventTypeRepeat, @@ -1288,15 +1294,15 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { .character = "∆", .synthesized = false, }; - FlutterEngineSendKeyEvent(engine.get(), &repeatEventWideChar, + FlutterEngineSendKeyEvent(engine.get(), &repeat_event_wide_char, [](bool handled, void* user_data){}, nullptr); message_latch->Wait(); - expect_key_event_eq(echoed_event, repeatEventWideChar); + ExpectKeyEventEq(echoed_event, repeat_event_wide_char); EXPECT_EQ(echoed_char, 0x2206llu); // An up event with no character, synthesized - const FlutterKeyEvent upEvent { + const FlutterKeyEvent up_event { .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000000, .type = kFlutterKeyEventTypeUp, @@ -1305,11 +1311,11 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { .character = nullptr, .synthesized = true, }; - FlutterEngineSendKeyEvent(engine.get(), &upEvent, + FlutterEngineSendKeyEvent(engine.get(), &up_event, [](bool handled, void* user_data){}, nullptr); message_latch->Wait(); - expect_key_event_eq(echoed_event, upEvent); + ExpectKeyEventEq(echoed_event, up_event); EXPECT_EQ(echoed_char, 0llu); } From 54479e38f3c2c847707e16087ff70a7efbc1f0e1 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 16:49:01 -0800 Subject: [PATCH 29/82] Format --- lib/ui/window/key_data.h | 4 +- lib/ui/window/key_data_packet.h | 4 +- lib/ui/window/platform_configuration.cc | 9 +- lib/ui/window/platform_configuration.h | 3 +- lib/ui/window/window.cc | 8 +- lib/ui/window/window.h | 2 +- runtime/runtime_controller.cc | 11 +- shell/common/engine.cc | 5 +- shell/common/platform_view.cc | 8 +- shell/common/platform_view.h | 6 +- shell/common/shell.cc | 9 +- shell/common/shell.h | 2 +- shell/platform/embedder/embedder.cc | 20 ++- shell/platform/embedder/embedder.h | 39 +++--- shell/platform/embedder/embedder_engine.h | 9 +- .../embedder/tests/embedder_unittests.cc | 125 +++++++++--------- 16 files changed, 130 insertions(+), 134 deletions(-) diff --git a/lib/ui/window/key_data.h b/lib/ui/window/key_data.h index a9220a7c225cb..5c84bf0cb58c8 100644 --- a/lib/ui/window/key_data.h +++ b/lib/ui/window/key_data.h @@ -24,8 +24,8 @@ enum class KeyEventType : int64_t { // The fixed-length sections of a KeyDataPacket. // -// KeyData does not contain `character`, for variable-length data are stored in a -// different way in KeyDataPacket. +// KeyData does not contain `character`, for variable-length data are stored in +// a different way in KeyDataPacket. // // This structure is unpacked by hooks.dart. struct alignas(8) KeyData { diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h index f340a891d0a29..e8efdf17334df 100644 --- a/lib/ui/window/key_data_packet.h +++ b/lib/ui/window/key_data_packet.h @@ -5,8 +5,8 @@ #ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_MESSAGE_H_ #define FLUTTER_LIB_UI_WINDOW_KEY_DATA_MESSAGE_H_ -#include #include +#include #include "flutter/fml/macros.h" #include "flutter/lib/ui/window/key_data.h" @@ -16,7 +16,7 @@ namespace flutter { // A byte stream representing a key event, to be sent to the framework. class KeyDataPacket { public: - // Build the key data packet by providing information. + // Build the key data packet by providing information. // // The `character` is a nullable C-string that ends with a '\0'. KeyDataPacket(const KeyData& event, const char* character); diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 024041e820699..44f389a880272 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -182,9 +182,8 @@ void GetPersistentIsolateData(Dart_NativeArguments args) { } void RespondToKeyData(Dart_Handle window, int response_id, bool handled) { - UIDartState::Current() - ->platform_configuration() - ->CompleteKeyDataResponse(response_id, handled); + UIDartState::Current()->platform_configuration()->CompleteKeyDataResponse( + response_id, handled); } void _RespondToKeyData(Dart_NativeArguments args) { @@ -366,7 +365,6 @@ uint64_t PlatformConfiguration::RegisterKeyDataResponse( return response_id; } - void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime) { std::shared_ptr dart_state = begin_frame_.dart_state().lock(); @@ -442,7 +440,8 @@ void PlatformConfiguration::CompletePlatformMessageResponse( response->Complete(std::make_unique(std::move(data))); } -void PlatformConfiguration::CompleteKeyDataResponse(uint64_t response_id, bool handled) { +void PlatformConfiguration::CompleteKeyDataResponse(uint64_t response_id, + bool handled) { if (response_id == 0) { return; } diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 2d8a800bd8b76..9b5f1b5135fa6 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -470,8 +470,7 @@ class PlatformConfiguration final { // ID starts at 1 because an ID of 0 indicates that no response is expected. uint64_t next_key_response_id_ = 1; - std::unordered_map - pending_key_responses_; + std::unordered_map pending_key_responses_; }; } // namespace flutter diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 650fe0b37bbec..994eca2ab5499 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -36,7 +36,8 @@ void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) { library_.value(), "_dispatchPointerDataPacket", {data_handle})); } -void Window::DispatchKeyDataPacket(const KeyDataPacket& packet, uint64_t response_id) { +void Window::DispatchKeyDataPacket(const KeyDataPacket& packet, + uint64_t response_id) { std::shared_ptr dart_state = library_.dart_state().lock(); if (!dart_state) return; @@ -48,8 +49,9 @@ void Window::DispatchKeyDataPacket(const KeyDataPacket& packet, uint64_t respons if (Dart_IsError(data_handle)) { return; } - tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_dispatchKeyData", - {data_handle, tonic::ToDart(response_id)})); + tonic::LogIfError( + tonic::DartInvokeField(library_.value(), "_dispatchKeyData", + {data_handle, tonic::ToDart(response_id)})); } void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index aae68604ffdeb..9a6847d3c46a6 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -34,7 +34,7 @@ class Window final { // Dispatch a packet to the framework that indicates a key event. // // The `response_id` is used to label the response of whether the key event - // is handled by the framework, typically the return value of + // is handled by the framework, typically the return value of // PlatformConfiguration::RegisterKeyDataResponse. // It should be used later in // PlatformConfiguration::CompleteKeyDataResponse. diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 45c7050abf690..33b977a7d9d8e 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -262,14 +262,15 @@ bool RuntimeController::DispatchPointerDataPacket( return false; } -bool RuntimeController::DispatchKeyDataPacket( - const KeyDataPacket& packet, - KeyDataResponse callback) { +bool RuntimeController::DispatchKeyDataPacket(const KeyDataPacket& packet, + KeyDataResponse callback) { if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataPacket", "mode", "basic"); - uint64_t response_id = platform_configuration->RegisterKeyDataResponse(std::move(callback)); - platform_configuration->get_window(0)->DispatchKeyDataPacket(packet, response_id); + uint64_t response_id = + platform_configuration->RegisterKeyDataResponse(std::move(callback)); + platform_configuration->get_window(0)->DispatchKeyDataPacket(packet, + response_id); return true; } return false; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 7fa1dbe38a440..669a7cf878567 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -415,9 +415,8 @@ void Engine::DispatchPointerDataPacket( pointer_data_dispatcher_->DispatchPacket(std::move(packet), trace_flow_id); } -void Engine::DispatchKeyDataPacket( - std::unique_ptr packet, - KeyDataResponse callback) { +void Engine::DispatchKeyDataPacket(std::unique_ptr packet, + KeyDataResponse callback) { TRACE_EVENT0("flutter", "Engine::DispatchKeyDataPacket"); if (runtime_controller_) { runtime_controller_->DispatchKeyDataPacket(*packet, std::move(callback)); diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 53a3726aaf0ca..27a398e5cdbcb 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -42,10 +42,10 @@ void PlatformView::DispatchPointerDataPacket( pointer_data_packet_converter_.Convert(std::move(packet))); } -void PlatformView::DispatchKeyDataPacket( - std::unique_ptr packet, - KeyDataResponse callback) { - delegate_.OnPlatformViewDispatchKeyDataPacket(std::move(packet), std::move(callback)); +void PlatformView::DispatchKeyDataPacket(std::unique_ptr packet, + KeyDataResponse callback) { + delegate_.OnPlatformViewDispatchKeyDataPacket(std::move(packet), + std::move(callback)); } void PlatformView::DispatchSemanticsAction(int32_t id, diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index ac8e7338dd925..ea75883702145 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -5,8 +5,8 @@ #ifndef COMMON_PLATFORM_VIEW_H_ #define COMMON_PLATFORM_VIEW_H_ -#include #include +#include #include "flow/embedded_views.h" #include "flutter/common/graphics/texture.h" @@ -54,7 +54,7 @@ class PlatformView { /// class Delegate { public: - using KeyDataResponse = std::function; + using KeyDataResponse = std::function; //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view was created /// with the given render surface. This surface is platform @@ -140,7 +140,7 @@ class PlatformView { /// virtual void OnPlatformViewDispatchKeyDataPacket( std::unique_ptr packet, - std::function callback) = 0; + std::function callback) = 0; //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view has encountered diff --git a/shell/common/shell.cc b/shell/common/shell.cc index d1adb4eedc1a7..fbf1e22cc89ae 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -964,15 +964,14 @@ void Shell::OnPlatformViewDispatchPointerDataPacket( // |PlatformView::Delegate| void Shell::OnPlatformViewDispatchKeyDataPacket( std::unique_ptr packet, - std::function callback) { + std::function callback) { TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataPacket"); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( - [engine = weak_engine_, - packet = std::move(packet), - callback = std::move(callback)]() mutable { + task_runners_.GetUITaskRunner()->PostTask( + fml::MakeCopyable([engine = weak_engine_, packet = std::move(packet), + callback = std::move(callback)]() mutable { if (engine) { engine->DispatchKeyDataPacket(std::move(packet), std::move(callback)); } diff --git a/shell/common/shell.h b/shell/common/shell.h index 716f5c72d3aaf..fab937891f8d1 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -507,7 +507,7 @@ class Shell final : public PlatformView::Delegate, // |PlatformView::Delegate| void OnPlatformViewDispatchKeyDataPacket( std::unique_ptr packet, - std::function callback) override; + std::function callback) override; // |PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction( diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index a3d3189b04fae..436e7bdef28df 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1492,7 +1492,8 @@ FlutterEngineResult FlutterEngineSendPointerEvent( "running Flutter application."); } -static inline flutter::KeyEventType MapKeyEventType(FlutterKeyEventType event_kind) { +static inline flutter::KeyEventType MapKeyEventType( + FlutterKeyEventType event_kind) { switch (event_kind) { case kFlutterKeyEventTypeUp: return flutter::KeyEventType::kUp; @@ -1514,11 +1515,11 @@ static inline flutter::KeyEventType MapKeyEventType(FlutterKeyEventType event_ki // i.e. 8 bytes. static constexpr size_t kKeyEventCharacterMaxBytes = 8; -FlutterEngineResult FlutterEngineSendKeyEvent( - FLUTTER_API_SYMBOL(FlutterEngine) engine, - const FlutterKeyEvent* event, - FlutterKeyEventCallback callback, - void* user_data) { +FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) + engine, + const FlutterKeyEvent* event, + FlutterKeyEventCallback callback, + void* user_data) { if (engine == nullptr) { return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); } @@ -1538,8 +1539,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( key_data.logical = SAFE_ACCESS(event, logical, 0); key_data.synthesized = SAFE_ACCESS(event, synthesized, false); - auto packet = std::make_unique( - key_data, character); + auto packet = std::make_unique(key_data, character); auto response = [callback, user_data](bool handled) { if (callback != nullptr) @@ -1547,9 +1547,7 @@ FlutterEngineResult FlutterEngineSendKeyEvent( }; return reinterpret_cast(engine) - ->DispatchKeyDataPacket( - std::move(packet), - response) + ->DispatchKeyDataPacket(std::move(packet), response) ? kSuccess : LOG_EMBEDDER_ERROR(kInternalInconsistency, "Could not dispatch the key event to the " diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 428d7d08ff9f3..4c0ba35a34e23 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -627,15 +627,16 @@ typedef enum { /// /// * Each key press sequence shall consist of one key down event (`kind` being /// `kFlutterKeyEventTypeDown`), zero or more repeat events, and one key up -/// event, representing a physical key button being pressed, held, and released. +/// event, representing a physical key button being pressed, held, and +/// released. /// * All events throughout a key press sequence shall have the same `physical` /// and `logical`. Having different `character`s is allowed. typedef struct { /// The size of this struct. Must be sizeof(FlutterKeyEvent). size_t struct_size; - /// The timestamp at which the key event was generated. The timestamp should be - /// specified in microseconds and the clock should be the same as that used by - /// `FlutterEngineGetCurrentTime`. + /// The timestamp at which the key event was generated. The timestamp should + /// be specified in microseconds and the clock should be the same as that used + /// by `FlutterEngineGetCurrentTime`. double timestamp; /// The event kind. FlutterKeyEventType type; @@ -656,11 +657,11 @@ typedef struct { /// /// The embedder is likely to skip events and/or construct new events that do /// not correspond to any native events in order to conform the regularity - /// of events (as documented in `FlutterKeyEvent`). An example is when a key up - /// is missed due to loss of window focus, on a platform that provides query to - /// key pressing status, the embedder might realize that the key has been - /// released at the next key event, and should construct a synthesized up event - /// immediately before the actual event. + /// of events (as documented in `FlutterKeyEvent`). An example is when a key + /// up is missed due to loss of window focus, on a platform that provides + /// query to key pressing status, the embedder might realize that the key has + /// been released at the next key event, and should construct a synthesized up + /// event immediately before the actual event. /// /// An event being synthesized means that the framework will not trust the /// `timestamp` of the event. @@ -1619,20 +1620,20 @@ FlutterEngineResult FlutterEngineSendPointerEvent( /// @param[in] event The event data to be sent. This function will no /// longer access `event` after returning. /// @param[in] callback The callback invoked by the engine when the -/// Flutter application has decided whether it handles -/// this event. Accepts nullptr. -/// @param[in] user_data The context associated with the callback. The exact -/// same value will used to invoke `callback`. Accepts -/// nullptr. +/// Flutter application has decided whether it +/// handles this event. Accepts nullptr. +/// @param[in] user_data The context associated with the callback. The +/// exact same value will used to invoke `callback`. +/// Accepts nullptr. /// /// @return The result of the call. /// FLUTTER_EXPORT -FlutterEngineResult FlutterEngineSendKeyEvent( - FLUTTER_API_SYMBOL(FlutterEngine) engine, - const FlutterKeyEvent* event, - FlutterKeyEventCallback callback, - void* user_data); +FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) + engine, + const FlutterKeyEvent* event, + FlutterKeyEventCallback callback, + void* user_data); FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendPlatformMessage( diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index bdff752a9eceb..55afebdba7367 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -63,10 +63,11 @@ class EmbedderEngine { //---------------------------------------------------------------------------- /// @brief Notifies the platform view that the embedder has sent it a key /// data packet. A key data packet contains one key event. This - /// call originates in the platform view and the shell has forwarded - /// the same to the engine on the UI task runner here. The platform - /// view will decide whether to handle this event, and send the - /// result using `callback`, which will be called exactly once. + /// call originates in the platform view and the shell has + /// forwarded the same to the engine on the UI task runner here. + /// The platform view will decide whether to handle this event, + /// and send the result using `callback`, which will be called + /// exactly once. /// /// @param[in] packet The key data packet. /// @param[in] callback Called when the framework has decided whether diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index faad0634141fd..bb53760433be4 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1205,7 +1205,7 @@ typedef struct { // It performs a revesed mapping from `_serializeKeyEventType` // in shell/platform/embedder/fixtures/main.dart. FlutterKeyEventType UnserializeKeyEventKind(uint64_t kind) { - switch(kind) { + switch (kind) { case 1: return kFlutterKeyEventTypeUp; case 2: @@ -1220,7 +1220,8 @@ FlutterKeyEventType UnserializeKeyEventKind(uint64_t kind) { // Checks the equality of two `FlutterKeyEvent` by each of their members except // for `character`. The `character` must be checked separately. -void ExpectKeyEventEq(const FlutterKeyEvent& subject, const FlutterKeyEvent& baseline) { +void ExpectKeyEventEq(const FlutterKeyEvent& subject, + const FlutterKeyEvent& baseline) { EXPECT_EQ(subject.timestamp, baseline.timestamp); EXPECT_EQ(subject.type, baseline.type); EXPECT_EQ(subject.physical, baseline.physical); @@ -1234,19 +1235,19 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { FlutterKeyEvent echoed_event; auto native_echo_event = [&](Dart_NativeArguments args) { - echoed_event.type = UnserializeKeyEventKind( - tonic::DartConverter::FromDart( + echoed_event.type = + UnserializeKeyEventKind(tonic::DartConverter::FromDart( Dart_GetNativeArgument(args, 0))); echoed_event.timestamp = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 1)); + Dart_GetNativeArgument(args, 1)); echoed_event.physical = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 2)); + Dart_GetNativeArgument(args, 2)); echoed_event.logical = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 3)); + Dart_GetNativeArgument(args, 3)); echoed_char = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 4)); - echoed_event.synthesized = tonic::DartConverter::FromDart( - Dart_GetNativeArgument(args, 5)); + Dart_GetNativeArgument(args, 4)); + echoed_event.synthesized = + tonic::DartConverter::FromDart(Dart_GetNativeArgument(args, 5)); message_latch->Signal(); }; @@ -1261,7 +1262,8 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { CREATE_NATIVE_ENTRY( [&ready](Dart_NativeArguments args) { ready.Signal(); })); - context.AddNativeCallback("EchoKeyEvent", CREATE_NATIVE_ENTRY(native_echo_event)); + context.AddNativeCallback("EchoKeyEvent", + CREATE_NATIVE_ENTRY(native_echo_event)); auto engine = builder.LaunchEngine(); ASSERT_TRUE(engine.is_valid()); @@ -1269,16 +1271,17 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { // A normal down event const FlutterKeyEvent down_event_upper_a { - .struct_size = sizeof(FlutterKeyEvent), - .timestamp = 1, - .type = kFlutterKeyEventTypeDown, - .physical = 0x00070004, - .logical = 0x00000000061, - .character = "A", - .synthesized = false, + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1, + .type = kFlutterKeyEventTypeDown, + .physical = 0x00070004, + .logical = 0x00000000061, + .character = "A", + .synthesized = false, }; - FlutterEngineSendKeyEvent(engine.get(), &down_event_upper_a, - [](bool handled, void* user_data){}, nullptr); + FlutterEngineSendKeyEvent( + engine.get(), &down_event_upper_a, [](bool handled, void* user_data){}, + nullptr); message_latch->Wait(); ExpectKeyEventEq(echoed_event, down_event_upper_a); @@ -1286,15 +1289,16 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { // A repeat event with multi-byte character const FlutterKeyEvent repeat_event_wide_char { - .struct_size = sizeof(FlutterKeyEvent), - .timestamp = 1000, - .type = kFlutterKeyEventTypeRepeat, - .physical = 0x00070005, - .logical = 0x00000000062, - .character = "∆", - .synthesized = false, + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1000, + .type = kFlutterKeyEventTypeRepeat, + .physical = 0x00070005, + .logical = 0x00000000062, + .character = "∆", + .synthesized = false, }; - FlutterEngineSendKeyEvent(engine.get(), &repeat_event_wide_char, + FlutterEngineSendKeyEvent( + engine.get(), &repeat_event_wide_char, [](bool handled, void* user_data){}, nullptr); message_latch->Wait(); @@ -1303,16 +1307,16 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { // An up event with no character, synthesized const FlutterKeyEvent up_event { - .struct_size = sizeof(FlutterKeyEvent), - .timestamp = 1000000, - .type = kFlutterKeyEventTypeUp, - .physical = 0x00070006, - .logical = 0x00000000063, - .character = nullptr, - .synthesized = true, + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1000000, + .type = kFlutterKeyEventTypeUp, + .physical = 0x00070006, + .logical = 0x00000000063, + .character = nullptr, + .synthesized = true, }; - FlutterEngineSendKeyEvent(engine.get(), &up_event, - [](bool handled, void* user_data){}, nullptr); + FlutterEngineSendKeyEvent( + engine.get(), &up_event, [](bool handled, void* user_data){}, nullptr); message_latch->Wait(); ExpectKeyEventEq(echoed_event, up_event); @@ -1330,8 +1334,8 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { CREATE_NATIVE_ENTRY( [&ready](Dart_NativeArguments args) { ready.Signal(); })); - context.AddNativeCallback("EchoKeyEvent", - CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {})); + context.AddNativeCallback( + "EchoKeyEvent", CREATE_NATIVE_ENTRY([](Dart_NativeArguments args) {})); auto engine = builder.LaunchEngine(); ASSERT_TRUE(engine.is_valid()); @@ -1339,24 +1343,24 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { // Dispatch a single event FlutterKeyEvent event { - .struct_size = sizeof(FlutterKeyEvent), - .timestamp = 1000, - .type = kFlutterKeyEventTypeDown, - .physical = 0x00070005, - .logical = 0x00000000062, - .character = nullptr, + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = 1000, + .type = kFlutterKeyEventTypeDown, + .physical = 0x00070005, + .logical = 0x00000000062, + .character = nullptr, }; KeyEventUserData user_data1 { - .latch = std::make_shared(), + .latch = std::make_shared(), }; // Entrypoint `key_data_echo` uses `event.synthesized` as `handled`. event.synthesized = true; FlutterEngineSendKeyEvent( - engine.get(), - &event, + engine.get(), &event, [](bool handled, void* untyped_user_data){ - KeyEventUserData* user_data = reinterpret_cast(untyped_user_data); + KeyEventUserData* user_data = + reinterpret_cast(untyped_user_data); EXPECT_EQ(handled, true); user_data->latch->Signal(); }, @@ -1366,31 +1370,24 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { // Dispatch two events back to back, using the same callback on different // user_data KeyEventUserData user_data2 { - .latch = std::make_shared(), - .returned = false, + .latch = std::make_shared(), + .returned = false, }; KeyEventUserData user_data3 { - .latch = std::make_shared(), - .returned = false, + .latch = std::make_shared(), + .returned = false, }; auto callback23 = [](bool handled, void* untyped_user_data){ - KeyEventUserData* user_data = reinterpret_cast(untyped_user_data); + KeyEventUserData* user_data = + reinterpret_cast(untyped_user_data); EXPECT_EQ(handled, false); user_data->latch->Signal(); user_data->returned = true; }; event.synthesized = false; - FlutterEngineSendKeyEvent( - engine.get(), - &event, - callback23, - &user_data2); - FlutterEngineSendKeyEvent( - engine.get(), - &event, - callback23, - &user_data3); + FlutterEngineSendKeyEvent(engine.get(), &event, callback23, &user_data2); + FlutterEngineSendKeyEvent(engine.get(), &event, callback23, &user_data3); user_data2.latch->Wait(); user_data3.latch->Wait(); EXPECT_TRUE(user_data2.returned); From a31d47512ccd2e2a177c3a12857d171238657bcf Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 16:55:48 -0800 Subject: [PATCH 30/82] Format --- lib/ui/window/platform_configuration.h | 2 +- .../embedder/tests/embedder_unittests.cc | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 9b5f1b5135fa6..e6dacbb535b57 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -23,7 +23,7 @@ class FontCollection; class PlatformMessage; class Scene; -typedef std::function KeyDataResponse; +typedef std::function KeyDataResponse; //-------------------------------------------------------------------------- /// @brief An enum for defining the different kinds of accessibility features diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index bb53760433be4..a8297a5a00056 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1270,7 +1270,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { ready.Wait(); // A normal down event - const FlutterKeyEvent down_event_upper_a { + const FlutterKeyEvent down_event_upper_a{ .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1, .type = kFlutterKeyEventTypeDown, @@ -1288,7 +1288,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { EXPECT_EQ(echoed_char, 0x41llu); // A repeat event with multi-byte character - const FlutterKeyEvent repeat_event_wide_char { + const FlutterKeyEvent repeat_event_wide_char{ .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000, .type = kFlutterKeyEventTypeRepeat, @@ -1299,14 +1299,14 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { }; FlutterEngineSendKeyEvent( engine.get(), &repeat_event_wide_char, - [](bool handled, void* user_data){}, nullptr); + [](bool handled, void* user_data) {}, nullptr); message_latch->Wait(); ExpectKeyEventEq(echoed_event, repeat_event_wide_char); EXPECT_EQ(echoed_char, 0x2206llu); // An up event with no character, synthesized - const FlutterKeyEvent up_event { + const FlutterKeyEvent up_event{ .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000000, .type = kFlutterKeyEventTypeUp, @@ -1316,7 +1316,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { .synthesized = true, }; FlutterEngineSendKeyEvent( - engine.get(), &up_event, [](bool handled, void* user_data){}, nullptr); + engine.get(), &up_event, [](bool handled, void* user_data) {}, nullptr); message_latch->Wait(); ExpectKeyEventEq(echoed_event, up_event); @@ -1342,7 +1342,7 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { ready.Wait(); // Dispatch a single event - FlutterKeyEvent event { + FlutterKeyEvent event{ .struct_size = sizeof(FlutterKeyEvent), .timestamp = 1000, .type = kFlutterKeyEventTypeDown, @@ -1351,14 +1351,14 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { .character = nullptr, }; - KeyEventUserData user_data1 { + KeyEventUserData user_data1{ .latch = std::make_shared(), }; // Entrypoint `key_data_echo` uses `event.synthesized` as `handled`. event.synthesized = true; FlutterEngineSendKeyEvent( engine.get(), &event, - [](bool handled, void* untyped_user_data){ + [](bool handled, void* untyped_user_data) { KeyEventUserData* user_data = reinterpret_cast(untyped_user_data); EXPECT_EQ(handled, true); @@ -1369,11 +1369,11 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { // Dispatch two events back to back, using the same callback on different // user_data - KeyEventUserData user_data2 { + KeyEventUserData user_data2{ .latch = std::make_shared(), .returned = false, }; - KeyEventUserData user_data3 { + KeyEventUserData user_data3{ .latch = std::make_shared(), .returned = false, }; From 9f192ee5ffdaed9e6a9d0ae73f41bd71f20be4cb Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 17:00:13 -0800 Subject: [PATCH 31/82] Format3 --- shell/platform/embedder/tests/embedder_unittests.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index a8297a5a00056..6826805dcd45c 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1280,7 +1280,7 @@ TEST_F(EmbedderTest, KeyDataIsCorrectlySerialized) { .synthesized = false, }; FlutterEngineSendKeyEvent( - engine.get(), &down_event_upper_a, [](bool handled, void* user_data){}, + engine.get(), &down_event_upper_a, [](bool handled, void* user_data) {}, nullptr); message_latch->Wait(); @@ -1377,7 +1377,7 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { .latch = std::make_shared(), .returned = false, }; - auto callback23 = [](bool handled, void* untyped_user_data){ + auto callback23 = [](bool handled, void* untyped_user_data) { KeyEventUserData* user_data = reinterpret_cast(untyped_user_data); EXPECT_EQ(handled, false); From bc406f97d509071bdfe2810392de09b8adf5c2e6 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 17:23:06 -0800 Subject: [PATCH 32/82] Remove unused --- shell/platform/embedder/embedder.cc | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 436e7bdef28df..00fd7633a2611 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1505,16 +1505,6 @@ static inline flutter::KeyEventType MapKeyEventType( return flutter::KeyEventType::kUp; } -// The number of bytes that should be able to fully store character data. -// -// This is an arbitrary number that is considered sufficient, used as an -// upperbound in strnlen. -// -// Many platforms assert the character to be less than 2 int16's, i.e. 4 bytes, -// therefore the character data is asserted to be less than double the amount, -// i.e. 8 bytes. -static constexpr size_t kKeyEventCharacterMaxBytes = 8; - FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterKeyEvent* event, From c6d5221469b9aaf6d0a7aebaf0d9a54e96e0c0e2 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 17:44:23 -0800 Subject: [PATCH 33/82] Fix web compile --- .../lib/src/engine/keyboard_binding.dart | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index 1a373e66a20bf..147077c5ac868 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -302,7 +302,7 @@ class KeyboardConverter { _keydownCancelDuration, () => ui.KeyData( timeStamp: currentTimeStamp + _keydownCancelDuration, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: physicalKey, logical: logicalKey, character: null, @@ -362,7 +362,7 @@ class KeyboardConverter { final int? lastLogicalRecord = _pressingRecords[physicalKey]; - ui.KeyEventType change; + ui.KeyEventType type; if (_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock) { // Case 1: Handle CapsLock on macOS @@ -374,7 +374,7 @@ class KeyboardConverter { Duration.zero, () => ui.KeyData( timeStamp: timeStamp, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: physicalKey, logical: logicalKey, character: null, @@ -384,16 +384,16 @@ class KeyboardConverter { _pressingRecords.remove(physicalKey); } ); - change = ui.KeyEventType.down; + type = ui.KeyEventType.down; } else if (isPhysicalDown) { // Case 2: Handle key down of normal keys - change = ui.KeyEventType.down; + type = ui.KeyEventType.down; if (lastLogicalRecord != null) { // This physical key is being pressed according to the record. if (event.repeat ?? false) { // A normal repeated key. - change = ui.KeyEventType.repeat; + type = ui.KeyEventType.repeat; } else { // A non-repeated key has been pressed that has the exact physical key as // a currently pressed one, usually indicating multiple keyboards are @@ -414,11 +414,11 @@ class KeyboardConverter { return false; } - change = ui.KeyEventType.up; + type = ui.KeyEventType.up; } final int? nextLogicalRecord; - switch (change) { + switch (type) { case ui.KeyEventType.down: assert(lastLogicalRecord == null); nextLogicalRecord = logicalKey; @@ -451,7 +451,7 @@ class KeyboardConverter { dispatchKeyData(ui.KeyData( timeStamp: timeStamp, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: physicalKey, logical: logicalKey, character: null, @@ -474,10 +474,10 @@ class KeyboardConverter { final ui.KeyData keyData = ui.KeyData( timeStamp: timeStamp, - change: change, + type: type, physical: physicalKey, logical: lastLogicalRecord ?? logicalKey, - character: change == ui.KeyEventType.up ? null : character, + character: type == ui.KeyEventType.up ? null : character, synthesized: false, ); From 54f3231a60eb2070b7365208654b75f7a66434ae Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 18:25:13 -0800 Subject: [PATCH 34/82] Add return value handling to web --- .../lib/src/engine/keyboard_binding.dart | 14 +- lib/web_ui/test/keyboard_converter_test.dart | 142 ++++++++++-------- 2 files changed, 87 insertions(+), 69 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index 147077c5ac868..7e6e46850e3a7 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -67,7 +67,7 @@ const int _kDeadKeyShift = 0x200000000000; const int _kDeadKeyAlt = 0x400000000000; const int _kDeadKeyMeta = 0x800000000000; -typedef DispatchKeyData = void Function(ui.KeyData data); +typedef DispatchKeyData = bool Function(ui.KeyData data); /// Converts a floating number timestamp (in milliseconds) to a [Duration] by /// splitting it into two integer components: milliseconds + microseconds. @@ -106,7 +106,7 @@ class KeyboardBinding { print(event.type); } if (EngineSemanticsOwner.instance.receiveGlobalEvent(event)) { - handler(event); + return handler(event); } }; assert(!_listeners.containsKey(eventName)); @@ -121,8 +121,11 @@ class KeyboardBinding { }); _listeners.clear(); } - void _onKeyData(ui.KeyData data) { - EnginePlatformDispatcher.instance.invokeOnKeyData(data); + bool _onKeyData(ui.KeyData data) { + bool? result; + EnginePlatformDispatcher.instance.invokeOnKeyData(data, + (bool handled) { result = handled; }); + return result!; } void _setup() { @@ -481,7 +484,6 @@ class KeyboardConverter { synthesized: false, ); - dispatchKeyData(keyData); - return true; + return dispatchKeyData(keyData); } } diff --git a/lib/web_ui/test/keyboard_converter_test.dart b/lib/web_ui/test/keyboard_converter_test.dart index daa09e9dd89c5..4f63cd1f456b5 100644 --- a/lib/web_ui/test/keyboard_converter_test.dart +++ b/lib/web_ui/test/keyboard_converter_test.dart @@ -49,6 +49,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); bool preventedDefault = false; final onPreventDefault = () { preventedDefault = true; }; @@ -59,7 +60,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(milliseconds: 1), - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -72,7 +73,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(milliseconds: 1, microseconds: 500), - change: ui.KeyEventType.repeat, + type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -85,7 +86,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(seconds: 1, milliseconds: 500), - change: ui.KeyEventType.repeat, + type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -98,7 +99,7 @@ void testMain() { ); expectKeyData(keyDataList.last, timeStamp: Duration(seconds: 2, microseconds: 500), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -110,11 +111,12 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -122,7 +124,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'A', kShift)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'A', @@ -130,7 +132,7 @@ void testMain() { converter.handleEvent(keyRepeatedDownEvent('KeyA', 'A', kShift)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.repeat, + type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'A', @@ -138,7 +140,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -146,7 +148,7 @@ void testMain() { converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.repeat, + type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -154,7 +156,7 @@ void testMain() { converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.repeat, + type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -162,7 +164,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -173,11 +175,12 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -185,7 +188,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, @@ -193,7 +196,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -201,7 +204,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, @@ -212,11 +215,12 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); converter.handleEvent(keyDownEvent('Digit1', '1', 0, 0)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalDigit1, logical: kLogicalDigit1, character: '1', @@ -224,7 +228,7 @@ void testMain() { converter.handleEvent(keyDownEvent('Numpad1', '1', 0, kLocationNumpad)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalNumpad1, logical: kLogicalNumpad1, character: '1', @@ -232,7 +236,7 @@ void testMain() { converter.handleEvent(keyUpEvent('Digit1', '1', 0, 0)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalDigit1, logical: kLogicalDigit1, character: null, @@ -240,7 +244,7 @@ void testMain() { converter.handleEvent(keyUpEvent('Numpad1', '1', 0, kLocationNumpad)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalNumpad1, logical: kLogicalNumpad1, character: null, @@ -251,13 +255,14 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); bool preventedDefault = false; final onPreventDefault = () { preventedDefault = true; }; converter.handleEvent(keyDownEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalTab, logical: kLogicalTab, character: null, @@ -267,7 +272,7 @@ void testMain() { converter.handleEvent(keyUpEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalTab, logical: kLogicalTab, character: null, @@ -280,6 +285,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); // The absolute values of the following logical keys are not guaranteed. @@ -294,7 +300,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyE, logical: kLogicalAltE, character: null, @@ -302,7 +308,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyE', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyE, logical: kLogicalAltE, character: null, @@ -310,7 +316,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyU', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyU, logical: kLogicalAltU, character: null, @@ -318,7 +324,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyU', 'Dead', kAlt)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyU, logical: kLogicalAltU, character: null, @@ -330,7 +336,7 @@ void testMain() { // testing. converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt | kShift)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyE, logical: kLogicalAltShiftE, character: null, @@ -340,7 +346,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyE', 'e', kShift)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyE, logical: kLogicalAltShiftE, character: null, @@ -353,6 +359,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); @@ -365,7 +372,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -376,6 +383,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); // A KeyDown of ShiftRight is missed due to loss of focus. @@ -387,6 +395,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); // Same layout @@ -405,7 +414,7 @@ void testMain() { keyDataList.clear(); converter.handleEvent(keyDownEvent('KeyA', 'a')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -413,7 +422,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyU', 'u')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyU, logical: kLogicalKeyU, character: 'u', @@ -424,12 +433,13 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }, onMacOs: true); converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -439,7 +449,7 @@ void testMain() { async.elapse(Duration(microseconds: 1)); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -450,7 +460,7 @@ void testMain() { converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -460,7 +470,7 @@ void testMain() { async.elapse(Duration(microseconds: 1)); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -472,7 +482,7 @@ void testMain() { converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -490,12 +500,13 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }, onMacOs: false); converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -508,7 +519,7 @@ void testMain() { converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -520,7 +531,7 @@ void testMain() { converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -528,7 +539,7 @@ void testMain() { converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalCapsLock, logical: kLogicalCapsLock, character: null, @@ -539,6 +550,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); @@ -547,7 +559,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a', kMeta)..timeStamp = 200); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 200), - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -559,7 +571,7 @@ void testMain() { async.elapse(Duration(milliseconds: 2500)); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 1200), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -570,7 +582,7 @@ void testMain() { converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 2700); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2700), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalMetaLeft, logical: kLogicalMetaLeft, character: null, @@ -581,7 +593,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2800), - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -591,7 +603,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2900), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -602,6 +614,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); @@ -621,7 +634,7 @@ void testMain() { async.elapse(Duration(milliseconds: 2500)); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 1700), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -632,7 +645,7 @@ void testMain() { converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 3200); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 3200), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalMetaLeft, logical: kLogicalMetaLeft, character: null, @@ -643,7 +656,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 3300); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 3300), - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -653,7 +666,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 3400); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 3400), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -664,6 +677,7 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }); converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); @@ -672,7 +686,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a', kCtrl)..timeStamp = 200); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 200), - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -686,7 +700,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 800); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 800), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -699,7 +713,7 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2800), - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -709,7 +723,7 @@ void testMain() { converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); expectKeyData(keyDataList.last, timeStamp: const Duration(milliseconds: 2900), - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: null, @@ -720,12 +734,13 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }, onMacOs: false); converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -738,7 +753,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -747,7 +762,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -755,7 +770,7 @@ void testMain() { converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); expectKeyData(keyDataList.last, - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalScrollLock, logical: kLogicalScrollLock, character: null, @@ -766,11 +781,12 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); + return true; }, onMacOs: false); converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, @@ -778,7 +794,7 @@ void testMain() { converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, @@ -790,21 +806,21 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')); expect(keyDataList, hasLength(3)); expectKeyData(keyDataList[0], - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, synthesized: true, ); expectKeyData(keyDataList[1], - change: ui.KeyEventType.up, + type: ui.KeyEventType.up, physical: kPhysicalShiftRight, logical: kLogicalShiftRight, character: null, synthesized: true, ); expectKeyData(keyDataList.last, - change: ui.KeyEventType.down, + type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', @@ -901,14 +917,14 @@ const kScrollLock = 0x4; void expectKeyData( ui.KeyData target, { - required ui.KeyEventType change, + required ui.KeyEventType type, required int physical, required int logical, required String? character, Duration? timeStamp, bool synthesized = false, }) { - expect(target.change, change); + expect(target.type, type); expect(target.physical, physical); expect(target.logical, logical); expect(target.character, character); From 5428e9942a1e03f2631747fb665ee79ee8cfdebd Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 19:05:29 -0800 Subject: [PATCH 35/82] Add some handling test --- .../lib/src/engine/keyboard_binding.dart | 28 ++--- lib/web_ui/test/keyboard_converter_test.dart | 118 +++++++++++------- 2 files changed, 81 insertions(+), 65 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index 7e6e46850e3a7..db98684332ae0 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -203,16 +203,6 @@ class KeyboardConverter { Duration get _keydownCancelDuration => onMacOs ? _kKeydownCancelDurationMacOs : _kKeydownCancelDurationNormal; - bool _shouldPreventDefault(FlutterHtmlKeyboardEvent event) { - switch (event.key) { - case 'Tab': - return true; - - default: - return false; - } - } - static int _getPhysicalCode(String code) { return kWebToPhysicalKey[code] ?? (code.hashCode + _kWebKeyIdPlane + _kAutogeneratedMask); } @@ -329,16 +319,13 @@ class KeyboardConverter { // * The method might dispatch some synthesized key data first to update states, // results discarded. // * Then it dispatches exactly one non-synthesized key data that corresponds - // to the `event`, i.e. the main key data. The result of this dispatching is - // returned to indicate whether the event is processed by Flutter. + // to the `event`, i.e. the primary key data. If this dispatching returns + // true, then this event will be invoked `preventDefault`. // * Some key data might be synthesized to update states after the main key // data. They are always scheduled asynchronously with results discarded. - bool handleEvent(FlutterHtmlKeyboardEvent event) { + void handleEvent(FlutterHtmlKeyboardEvent event) { final Duration timeStamp = _eventTimeStampToDuration(event.timeStamp!); - if (_shouldPreventDefault(event)) { - event.preventDefault(); - } final String eventKey = event.key!; final int physicalKey = _getPhysicalCode(event.code!); @@ -402,7 +389,7 @@ class KeyboardConverter { // a currently pressed one, usually indicating multiple keyboards are // pressing keys with the same physical key, or the up event was lost // during a loss of focus. The down event is ignored. - return false; + return; } } else { // This physical key is not being pressed according to the record. It's a @@ -414,7 +401,7 @@ class KeyboardConverter { if (lastLogicalRecord == null) { // The physical key has been released before. It indicates multiple // keyboards pressed keys with the same physical key. Ignore the up event. - return false; + return; } type = ui.KeyEventType.up; @@ -484,6 +471,9 @@ class KeyboardConverter { synthesized: false, ); - return dispatchKeyData(keyData); + bool primaryHandled = dispatchKeyData(keyData); + if (primaryHandled) { + event.preventDefault(); + } } } diff --git a/lib/web_ui/test/keyboard_converter_test.dart b/lib/web_ui/test/keyboard_converter_test.dart index 4f63cd1f456b5..8c21267925a02 100644 --- a/lib/web_ui/test/keyboard_converter_test.dart +++ b/lib/web_ui/test/keyboard_converter_test.dart @@ -49,7 +49,8 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); - return true; + // Only handle down events + return key.type == ui.KeyEventType.down; }); bool preventedDefault = false; final onPreventDefault = () { preventedDefault = true; }; @@ -65,7 +66,8 @@ void testMain() { logical: kLogicalKeyA, character: 'a', ); - expect(preventedDefault, false); + expect(preventedDefault, true); + preventedDefault = false; converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a') ..timeStamp = 1.5 @@ -111,56 +113,79 @@ void testMain() { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); - return true; + // Only handle down events + return key.type == ui.KeyEventType.down; }); + bool preventedDefault = false; + final onPreventDefault = () { preventedDefault = true; }; - converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); expectKeyData(keyDataList.last, type: ui.KeyEventType.down, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, ); + expect(preventedDefault, true); + preventedDefault = false; - converter.handleEvent(keyDownEvent('KeyA', 'A', kShift)); + converter.handleEvent(keyDownEvent('KeyA', 'A', kShift) + ..onPreventDefault = onPreventDefault + ); expectKeyData(keyDataList.last, type: ui.KeyEventType.down, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'A', ); + expect(preventedDefault, true); + preventedDefault = false; - converter.handleEvent(keyRepeatedDownEvent('KeyA', 'A', kShift)); + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'A', kShift) + ..onPreventDefault = onPreventDefault + ); expectKeyData(keyDataList.last, type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'A', ); + expect(preventedDefault, false); - converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); expectKeyData(keyDataList.last, type: ui.KeyEventType.up, physical: kPhysicalShiftLeft, logical: kLogicalShiftLeft, character: null, ); + expect(preventedDefault, false); - converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a') + ..onPreventDefault = onPreventDefault + ); expectKeyData(keyDataList.last, type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', ); + expect(preventedDefault, false); - converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a') + ..onPreventDefault = onPreventDefault + ); expectKeyData(keyDataList.last, type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, logical: kLogicalKeyA, character: 'a', ); + expect(preventedDefault, false); converter.handleEvent(keyUpEvent('KeyA', 'a')); expectKeyData(keyDataList.last, @@ -169,6 +194,7 @@ void testMain() { logical: kLogicalKeyA, character: null, ); + expect(preventedDefault, false); }); test('Distinguish between left and right modifiers', () { @@ -251,36 +277,6 @@ void testMain() { ); }); - test('Prevents default when pressing Tab', () { - final List keyDataList = []; - final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { - keyDataList.add(key); - return true; - }); - bool preventedDefault = false; - final onPreventDefault = () { preventedDefault = true; }; - - converter.handleEvent(keyDownEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); - expectKeyData(keyDataList.last, - type: ui.KeyEventType.down, - physical: kPhysicalTab, - logical: kLogicalTab, - character: null, - ); - expect(preventedDefault, true); - preventedDefault = false; - - converter.handleEvent(keyUpEvent('Tab', 'Tab')..onPreventDefault = onPreventDefault); - expectKeyData(keyDataList.last, - type: ui.KeyEventType.up, - physical: kPhysicalTab, - logical: kLogicalTab, - character: null, - ); - expect(preventedDefault, true); - preventedDefault = false; - }); - test('Dead keys are distinguishable', () { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { @@ -361,15 +357,26 @@ void testMain() { keyDataList.add(key); return true; }); + bool preventedDefault = false; + final onPreventDefault = () { preventedDefault = true; }; - converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); + expect(preventedDefault, true); + preventedDefault = false; // A KeyUp of ShiftLeft is missed due to loss of focus. keyDataList.clear(); - converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); expect(keyDataList, isEmpty); + expect(preventedDefault, false); - converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, type: ui.KeyEventType.up, @@ -377,6 +384,7 @@ void testMain() { logical: kLogicalShiftLeft, character: null, ); + expect(preventedDefault, true); }); test('Duplicate ups are skipped', () { @@ -385,10 +393,15 @@ void testMain() { keyDataList.add(key); return true; }); + bool preventedDefault = false; + final onPreventDefault = () { preventedDefault = true; }; // A KeyDown of ShiftRight is missed due to loss of focus. - converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight)); + converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight) + ..onPreventDefault = onPreventDefault + ); expect(keyDataList, isEmpty); + expect(preventedDefault, false); }); test('Conflict from multiple keyboards do not crash', () { @@ -435,8 +448,13 @@ void testMain() { keyDataList.add(key); return true; }, onMacOs: true); + bool preventedDefault = false; + final onPreventDefault = () { preventedDefault = true; }; - converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); + // A KeyDown of ShiftRight is missed due to loss of focus. + converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock') + ..onPreventDefault = onPreventDefault + ); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, type: ui.KeyEventType.down, @@ -444,7 +462,9 @@ void testMain() { logical: kLogicalCapsLock, character: null, ); + expect(preventedDefault, true); keyDataList.clear(); + preventedDefault = false; async.elapse(Duration(microseconds: 1)); expect(keyDataList, hasLength(1)); @@ -455,9 +475,12 @@ void testMain() { character: null, synthesized: true, ); + expect(preventedDefault, false); keyDataList.clear(); - converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); + converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock') + ..onPreventDefault = onPreventDefault + ); expect(keyDataList, hasLength(1)); expectKeyData(keyDataList.last, type: ui.KeyEventType.down, @@ -465,7 +488,9 @@ void testMain() { logical: kLogicalCapsLock, character: null, ); + expect(preventedDefault, true); keyDataList.clear(); + preventedDefault = false; async.elapse(Duration(microseconds: 1)); expect(keyDataList, hasLength(1)); @@ -476,6 +501,7 @@ void testMain() { character: null, synthesized: true, ); + expect(preventedDefault, false); keyDataList.clear(); // Another key down works @@ -777,7 +803,7 @@ void testMain() { ); }); - test('Deduce modifier key cancel from modifier field', () { + test('Deduce modifier key up from modifier field', () { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { keyDataList.add(key); From f2fd10dff1b83ff35c02d8de318c2c4705768cfb Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 25 Jan 2021 19:09:11 -0800 Subject: [PATCH 36/82] Fix signatures --- .../ios/framework/Source/FlutterEnginePlatformViewTest.mm | 3 ++- .../darwin/ios/framework/Source/FlutterPlatformViewsTest.mm | 3 ++- .../darwin/ios/framework/Source/accessibility_bridge_test.mm | 3 ++- shell/platform/fuchsia/flutter/platform_view_unittest.cc | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm index 129cce47c8199..53d8688077068 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm @@ -25,7 +25,8 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet, + std::function callback) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 631f98c30c887..c826a2e9723d2 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -95,7 +95,8 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet, + std::function callback) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index a446224495925..41f8072afd858 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -78,7 +78,8 @@ void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override { void OnPlatformViewDispatchPlatformMessage(fml::RefPtr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { } - void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet) override {} + void OnPlatformViewDispatchKeyDataPacket(std::unique_ptr packet, + std::function callback) override {} void OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) override {} diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 1f6b793f1d160..608c133a2bd8c 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -82,7 +82,8 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { std::unique_ptr packet) {} // |flutter::PlatformView::Delegate| void OnPlatformViewDispatchKeyDataPacket( - std::unique_ptr packet) {} + std::unique_ptr packet, + std::function callback) {} // |flutter::PlatformView::Delegate| void OnPlatformViewDispatchSemanticsAction(int32_t id, flutter::SemanticsAction action, From bf643a58042146706bc637dcae8842e35e8e8e58 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 26 Jan 2021 13:11:36 -0800 Subject: [PATCH 37/82] Better doc --- shell/platform/embedder/embedder.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 4c0ba35a34e23..48ce6de82975e 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -663,8 +663,8 @@ typedef struct { /// been released at the next key event, and should construct a synthesized up /// event immediately before the actual event. /// - /// An event being synthesized means that the framework will not trust the - /// `timestamp` of the event. + /// An event being synthesized means that the `timestamp` might greatly deviate + /// from the actual time when the event occurs physically. bool synthesized; } FlutterKeyEvent; From 771dfe610cdbb4ef2de15ebadfe99e87566dcf9d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 26 Jan 2021 13:16:55 -0800 Subject: [PATCH 38/82] format --- shell/platform/embedder/embedder.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 48ce6de82975e..6489edcf938ef 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -663,8 +663,8 @@ typedef struct { /// been released at the next key event, and should construct a synthesized up /// event immediately before the actual event. /// - /// An event being synthesized means that the `timestamp` might greatly deviate - /// from the actual time when the event occurs physically. + /// An event being synthesized means that the `timestamp` might greatly + /// deviate from the actual time when the event occurs physically. bool synthesized; } FlutterKeyEvent; From 749c16170e730f61915f9f345bb334a787d921a8 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 26 Jan 2021 13:33:36 -0800 Subject: [PATCH 39/82] Deflake --- shell/platform/embedder/tests/embedder_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 6826805dcd45c..64b3bfed8fb87 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1381,8 +1381,8 @@ TEST_F(EmbedderTest, KeyDataResponseIsCorrectlyInvoked) { KeyEventUserData* user_data = reinterpret_cast(untyped_user_data); EXPECT_EQ(handled, false); - user_data->latch->Signal(); user_data->returned = true; + user_data->latch->Signal(); }; event.synthesized = false; From 7e2563e49dc8631407b9d79c1f271b12f421c369 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 28 Jan 2021 03:51:43 -0800 Subject: [PATCH 40/82] Compiled (except for tests) --- lib/ui/hooks.dart | 4 +- shell/platform/embedder/embedder.h | 6 - shell/platform/windows/BUILD.gn | 18 +- shell/platform/windows/flutter_key_map.cc | 8 +- .../windows/flutter_keyboard_manager.h | 68 ----- .../windows/flutter_windows_engine.cc | 6 +- .../platform/windows/flutter_windows_engine.h | 4 +- .../platform/windows/flutter_windows_view.cc | 35 ++- shell/platform/windows/flutter_windows_view.h | 18 +- shell/platform/windows/key_event_handler.cc | 260 ------------------ ...hook_handler.h => keyboard_handler_base.h} | 6 +- .../windows/keyboard_key_channel_handler.cc | 140 ++++++++++ .../windows/keyboard_key_channel_handler.h | 45 +++ ...keyboard_key_channel_handler_unittests.cc} | 236 ++++++++-------- ...er.cc => keyboard_key_embedder_handler.cc} | 92 ++++--- .../windows/keyboard_key_embedder_handler.h | 83 ++++++ ...eyboard_key_embedder_handler_unittests.cc} | 31 ++- .../platform/windows/keyboard_key_handler.cc | 173 ++++++++++++ ...event_handler.h => keyboard_key_handler.h} | 60 ++-- .../windows/keyboard_key_handler_unittests.cc | 118 ++++++++ shell/platform/windows/text_input_plugin.cc | 2 +- shell/platform/windows/text_input_plugin.h | 16 +- .../platform/windows/win32_flutter_window.cc | 4 +- shell/platform/windows/win32_flutter_window.h | 2 +- .../windows/win32_flutter_window_unittests.cc | 17 +- shell/platform/windows/win32_window.cc | 7 +- shell/platform/windows/win32_window.h | 2 +- .../windows/win32_window_unittests.cc | 8 +- .../windows/window_binding_handler_delegate.h | 2 +- 29 files changed, 877 insertions(+), 594 deletions(-) delete mode 100644 shell/platform/windows/flutter_keyboard_manager.h delete mode 100644 shell/platform/windows/key_event_handler.cc rename shell/platform/windows/{keyboard_hook_handler.h => keyboard_handler_base.h} (94%) create mode 100644 shell/platform/windows/keyboard_key_channel_handler.cc create mode 100644 shell/platform/windows/keyboard_key_channel_handler.h rename shell/platform/windows/{key_event_handler_unittests.cc => keyboard_key_channel_handler_unittests.cc} (97%) rename shell/platform/windows/{flutter_keyboard_manager.cc => keyboard_key_embedder_handler.cc} (70%) create mode 100644 shell/platform/windows/keyboard_key_embedder_handler.h rename shell/platform/windows/{flutter_keyboard_manager_unittests.cc => keyboard_key_embedder_handler_unittests.cc} (83%) create mode 100644 shell/platform/windows/keyboard_key_handler.cc rename shell/platform/windows/{key_event_handler.h => keyboard_key_handler.h} (55%) create mode 100644 shell/platform/windows/keyboard_key_handler_unittests.cc diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index 35e366ca2cbc9..f3e8725884357 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -98,8 +98,8 @@ void _dispatchPointerDataPacket(ByteData packet) { @pragma('vm:entry-point') // ignore: unused_element -void _dispatchKeyData(ByteData packet) { - PlatformDispatcher.instance._dispatchKeyData(packet); +void _dispatchKeyData(ByteData packet, int responseId) { + PlatformDispatcher.instance._dispatchKeyData(packet, responseId); } @pragma('vm:entry-point') diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index cedc3d606cbff..6489edcf938ef 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -1635,12 +1635,6 @@ FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine) FlutterKeyEventCallback callback, void* user_data); -FLUTTER_EXPORT -FlutterEngineResult FlutterEngineSendKeyEvent( - FLUTTER_API_SYMBOL(FlutterEngine) engine, - const FlutterKeyEvent* event, - FlutterPlatformKeyEventResponseHandle** response_out); - FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendPlatformMessage( FLUTTER_API_SYMBOL(FlutterEngine) engine, diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index ceeedd7025fb9..5a6ba55c43247 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -51,8 +51,6 @@ source_set("flutter_windows_source") { "external_texture_gl.cc", "external_texture_gl.h", "flutter_key_map.cc", - "flutter_keyboard_manager.cc", - "flutter_keyboard_manager.h", "flutter_project_bundle.cc", "flutter_project_bundle.h", "flutter_windows.cc", @@ -62,9 +60,13 @@ source_set("flutter_windows_source") { "flutter_windows_texture_registrar.h", "flutter_windows_view.cc", "flutter_windows_view.h", - "key_event_handler.cc", - "key_event_handler.h", - "keyboard_hook_handler.h", + "keyboard_handler_base.h", + "keyboard_key_embedder_handler.cc", + "keyboard_key_embedder_handler.h", + "keyboard_key_channel_handler.cc", + "keyboard_key_channel_handler.h", + "keyboard_key_handler.cc", + "keyboard_key_handler.h", "platform_handler.cc", "platform_handler.h", "string_conversion.cc", @@ -172,11 +174,11 @@ if (target_os == "winuwp") { testonly = true sources = [ - "flutter_keyboard_manager_unittests.cc", "flutter_project_bundle_unittests.cc", "flutter_windows_engine_unittests.cc", "flutter_windows_texture_registrar_unittests.cc", - "key_event_handler_unittests.cc", + # "keyboard_key_embedder_handler_unittests.cc", + # "key_event_handler_unittests.cc", "string_conversion_unittests.cc", "system_utils_unittests.cc", "testing/engine_embedder_api_modifier.h", @@ -187,7 +189,7 @@ if (target_os == "winuwp") { "testing/win32_flutter_window_test.cc", "testing/win32_flutter_window_test.h", "win32_dpi_utils_unittests.cc", - "win32_flutter_window_unittests.cc", + # "win32_flutter_window_unittests.cc", "win32_window_proc_delegate_manager_unittests.cc", "win32_window_unittests.cc", ] diff --git a/shell/platform/windows/flutter_key_map.cc b/shell/platform/windows/flutter_key_map.cc index be3df5ef8e5f8..f5dac22e16dc7 100644 --- a/shell/platform/windows/flutter_key_map.cc +++ b/shell/platform/windows/flutter_key_map.cc @@ -5,7 +5,7 @@ #ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_KEY_MAP_H_ #define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_KEY_MAP_H_ -#include "flutter/shell/platform/windows/flutter_keyboard_manager.h" +#include "flutter/shell/platform/windows/keyboard_key_embedder_handler.h" #include @@ -19,7 +19,7 @@ namespace flutter { -std::map FlutterKeyboardManager::windowsToPhysicalMap_ = { +std::map FlutterKeyboardKeyEmbedderHandler::windowsToPhysicalMap_ = { {0x0000e05f, 0x00010082}, // sleep {0x0000e063, 0x00010083}, // wakeUp {0x000000ff, 0x00070001}, // usbErrorRollOver @@ -179,7 +179,7 @@ std::map FlutterKeyboardManager::windowsToPhysicalMap_ = { {0x0000e066, 0x000c022a}, // browserFavorites }; -std::map FlutterKeyboardManager::windowsToLogicalMap_ = { +std::map FlutterKeyboardKeyEmbedderHandler::windowsToLogicalMap_ = { {0x00000008, 0x00000000008}, // BACK {0x00000009, 0x00000000009}, // TAB {0x0000000d, 0x0000000000d}, // RETURN @@ -300,7 +300,7 @@ std::map FlutterKeyboardManager::windowsToLogicalMap_ = { {0x000000a1, 0x0040000010d}, // RSHIFT }; -std::map FlutterKeyboardManager::scanCodeToLogicalMap_ = { +std::map FlutterKeyboardKeyEmbedderHandler::scanCodeToLogicalMap_ = { {0x0000e01d, 0x0400000105}, // ControlRight {0x0000e038, 0x0400000102}, // AltRight {0x0000004f, 0x0200000031}, // Numpad1 diff --git a/shell/platform/windows/flutter_keyboard_manager.h b/shell/platform/windows/flutter_keyboard_manager.h deleted file mode 100644 index e1a97c24273bb..0000000000000 --- a/shell/platform/windows/flutter_keyboard_manager.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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_WINDOWS_FLUTTER_KEYBOARD_MANAGER_H_ -#define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_KEYBOARD_MANAGER_H_ - -#include -#include -#include -#include - -#include "flutter/shell/platform/embedder/embedder.h" -#include "flutter/shell/platform/windows/keyboard_hook_handler.h" -#include "flutter/shell/platform/windows/public/flutter_windows.h" - -namespace flutter { - -namespace { -// An arbitrary size for the character cache in bytes. -// -// It should hold a UTF-32 character encoded in UTF-8 as long as the trailing -// '\0'. -constexpr size_t kCharacterCacheSize = 8; -} // namespace - -class FlutterWindowsView; - -// Implements a KeyboardHookHandler -// -// Handles key events and forwards them to the Flutter engine. -class FlutterKeyboardManager : public KeyboardHookHandler { - public: - explicit FlutterKeyboardManager( - std::function onEvent); - - virtual ~FlutterKeyboardManager(); - - // |KeyboardHookHandler| - void KeyboardHook(FlutterWindowsView* window, - int key, - int scancode, - int action, - char32_t character, - bool wasDown) override; - - // |KeyboardHookHandler| - void TextHook(FlutterWindowsView* window, - const std::u16string& text) override; - - private: - void cacheUtf8String(char32_t ch); - - std::function onEvent_; - std::map pressingRecords_; - char character_cache_[kCharacterCacheSize]; - - static uint64_t getPhysicalKey(int scancode); - static uint64_t getLogicalKey(int key, int scancode); - - static std::map windowsToPhysicalMap_; - static std::map windowsToLogicalMap_; - static std::map scanCodeToLogicalMap_; -}; - -} // namespace flutter - -#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_KEYBOARD_MANAGER_H_ diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 0638ae2525703..cb006256823c8 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -290,9 +290,11 @@ void FlutterWindowsEngine::SendPointerEvent(const FlutterPointerEvent& event) { } } -void FlutterWindowsEngine::SendKeyEvent(const FlutterKeyEvent& event) { +void FlutterWindowsEngine::SendKeyEvent(const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, + void* user_data) { if (engine_) { - embedder_api_.SendKeyEvent(engine_, &event); + embedder_api_.SendKeyEvent(engine_, &event, callback, user_data); } } diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index c170ed2c5b918..f108e184a300f 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -98,7 +98,9 @@ class FlutterWindowsEngine { void SendPointerEvent(const FlutterPointerEvent& event); // Informs the engine of an incoming key event. - void SendKeyEvent(const FlutterKeyEvent& event); + void SendKeyEvent(const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, + void* user_data); // Sends the given message to the engine, calling |reply| with |user_data| // when a reponse is received from the engine if they are non-null. diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index a2d06d835021a..38c12f9e8498b 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -6,7 +6,9 @@ #include -#include "flutter/shell/platform/windows/flutter_keyboard_manager.h" +#include "flutter/shell/platform/windows/text_input_plugin.h" +#include "flutter/shell/platform/windows/keyboard_key_channel_handler.h" +#include "flutter/shell/platform/windows/keyboard_key_embedder_handler.h" namespace flutter { @@ -37,7 +39,7 @@ void FlutterWindowsView::SetEngine( // Set up the system channel handlers. auto internal_plugin_messenger = internal_plugin_registrar_->messenger(); - RegisterKeyboardHookHandlers(internal_plugin_messenger); + RegisterKeyboardHandlers(internal_plugin_messenger); platform_handler_ = PlatformHandler::Create(internal_plugin_messenger, this); cursor_handler_ = std::make_unique( internal_plugin_messenger, binding_handler_.get()); @@ -48,19 +50,24 @@ void FlutterWindowsView::SetEngine( binding_handler_->GetDpiScale()); } -void FlutterWindowsView::RegisterKeyboardHookHandlers( +void FlutterWindowsView::RegisterKeyboardHandlers( flutter::BinaryMessenger* messenger) { - AddKeyboardHookHandler(std::make_unique( - [this](const FlutterKeyEvent& event) { - engine_->SendKeyEvent(event); + auto key_handler = std::make_unique(); + key_handler->AddDelegate(std::make_unique( + messenger)); + key_handler->AddDelegate(std::make_unique( + [this](const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, + void* user_data) { + return engine_->SendKeyEvent(event, callback, user_data); })); - AddKeyboardHookHandler(std::make_unique(messenger)); - AddKeyboardHookHandler( + AddKeyboardHandlerBase(std::move(key_handler)); + AddKeyboardHandlerBase( std::make_unique(messenger, this)); } -void FlutterWindowsView::AddKeyboardHookHandler( - std::unique_ptr handler) { +void FlutterWindowsView::AddKeyboardHandlerBase( + std::unique_ptr handler) { keyboard_hook_handlers_.push_back(std::move(handler)); } @@ -140,8 +147,8 @@ bool FlutterWindowsView::OnKey(int key, int action, char32_t character, bool extended, - bool wasDown) { - return SendKey(key, scancode, action, character, extended, wasDown); + bool was_down) { + return SendKey(key, scancode, action, character, extended, was_down); } void FlutterWindowsView::OnComposeBegin() { @@ -250,10 +257,10 @@ bool FlutterWindowsView::SendKey(int key, int action, char32_t character, bool extended, - bool wasDown) { + bool was_down) { for (const auto& handler : keyboard_hook_handlers_) { if (handler->KeyboardHook(this, key, scancode, action, character, - extended, wasDown)) { + extended, was_down)) { // key event was handled, so don't send to other handlers. return true; } diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 311bb6078b689..40edb6ff8a459 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -18,11 +18,9 @@ #include "flutter/shell/platform/windows/angle_surface_manager.h" #include "flutter/shell/platform/windows/cursor_handler.h" #include "flutter/shell/platform/windows/flutter_windows_engine.h" -#include "flutter/shell/platform/windows/key_event_handler.h" -#include "flutter/shell/platform/windows/keyboard_hook_handler.h" +#include "flutter/shell/platform/windows/keyboard_handler_base.h" #include "flutter/shell/platform/windows/platform_handler.h" #include "flutter/shell/platform/windows/public/flutter_windows.h" -#include "flutter/shell/platform/windows/text_input_plugin.h" #include "flutter/shell/platform/windows/text_input_plugin_delegate.h" #include "flutter/shell/platform/windows/window_binding_handler.h" #include "flutter/shell/platform/windows/window_binding_handler_delegate.h" @@ -105,7 +103,7 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, int action, char32_t character, bool extended, - bool wasDown) override; + bool was_down) override; // |WindowBindingHandlerDelegate| void OnComposeBegin() override; @@ -128,12 +126,12 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, protected: // Called to create the keyboard hook handlers. - virtual void RegisterKeyboardHookHandlers( + virtual void RegisterKeyboardHandlers( flutter::BinaryMessenger* messenger); - // Used by RegisterKeyboardHookHandlers to add a new keyboard hook handler. - void AddKeyboardHookHandler( - std::unique_ptr handler); + // Used by RegisterKeyboardHandlers to add a new keyboard hook handler. + void AddKeyboardHandlerBase( + std::unique_ptr handler); private: // Struct holding the mouse state. The engine doesn't keep track of which @@ -194,7 +192,7 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, int action, char32_t character, bool extended, - bool wasDown); + bool was_down); // Reports an IME compose begin event. // @@ -268,7 +266,7 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, std::unique_ptr internal_plugin_registrar_; // Handlers for keyboard events from Windows. - std::vector> + std::vector> keyboard_hook_handlers_; // Handler for the flutter/platform channel. diff --git a/shell/platform/windows/key_event_handler.cc b/shell/platform/windows/key_event_handler.cc deleted file mode 100644 index 19b024a2c61bb..0000000000000 --- a/shell/platform/windows/key_event_handler.cc +++ /dev/null @@ -1,260 +0,0 @@ -// 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/windows/key_event_handler.h" - -#include - -#include - -#include "flutter/shell/platform/common/cpp/json_message_codec.h" - -namespace flutter { - -namespace { - -static constexpr char kChannelName[] = "flutter/keyevent"; - -static constexpr char kKeyCodeKey[] = "keyCode"; -static constexpr char kScanCodeKey[] = "scanCode"; -static constexpr char kCharacterCodePointKey[] = "characterCodePoint"; -static constexpr char kModifiersKey[] = "modifiers"; -static constexpr char kKeyMapKey[] = "keymap"; -static constexpr char kTypeKey[] = "type"; -static constexpr char kHandledKey[] = "handled"; - -static constexpr char kWindowsKeyMap[] = "windows"; -static constexpr char kKeyUp[] = "keyup"; -static constexpr char kKeyDown[] = "keydown"; - -// The maximum number of pending events to keep before -// emitting a warning on the console about unhandled events. -static constexpr int kMaxPendingEvents = 1000; - -// Re-definition of the modifiers for compatibility with the Flutter framework. -// These have to be in sync with the framework's RawKeyEventDataWindows -// modifiers definition. -// https://github.com/flutter/flutter/blob/19ff596979e407c484a32f4071420fca4f4c885f/packages/flutter/lib/src/services/raw_keyboard_windows.dart#L203 -static constexpr int kShift = 1 << 0; -static constexpr int kShiftLeft = 1 << 1; -static constexpr int kShiftRight = 1 << 2; -static constexpr int kControl = 1 << 3; -static constexpr int kControlLeft = 1 << 4; -static constexpr int kControlRight = 1 << 5; -static constexpr int kAlt = 1 << 6; -static constexpr int kAltLeft = 1 << 7; -static constexpr int kAltRight = 1 << 8; -static constexpr int kWinLeft = 1 << 9; -static constexpr int kWinRight = 1 << 10; -static constexpr int kCapsLock = 1 << 11; -static constexpr int kNumLock = 1 << 12; -static constexpr int kScrollLock = 1 << 13; - -/// Calls GetKeyState() an all modifier keys and packs the result in an int, -/// with the re-defined values declared above for compatibility with the Flutter -/// framework. -int GetModsForKeyState() { - int mods = 0; - - if (GetKeyState(VK_SHIFT) < 0) - mods |= kShift; - if (GetKeyState(VK_LSHIFT) < 0) - mods |= kShiftLeft; - if (GetKeyState(VK_RSHIFT) < 0) - mods |= kShiftRight; - if (GetKeyState(VK_CONTROL) < 0) - mods |= kControl; - if (GetKeyState(VK_LCONTROL) < 0) - mods |= kControlLeft; - if (GetKeyState(VK_RCONTROL) < 0) - mods |= kControlRight; - if (GetKeyState(VK_MENU) < 0) - mods |= kAlt; - if (GetKeyState(VK_LMENU) < 0) - mods |= kAltLeft; - if (GetKeyState(VK_RMENU) < 0) - mods |= kAltRight; - if (GetKeyState(VK_LWIN) < 0) - mods |= kWinLeft; - if (GetKeyState(VK_RWIN) < 0) - mods |= kWinRight; - if (GetKeyState(VK_CAPITAL) < 0) - mods |= kCapsLock; - if (GetKeyState(VK_NUMLOCK) < 0) - mods |= kNumLock; - if (GetKeyState(VK_SCROLL) < 0) - mods |= kScrollLock; - return mods; -} - -// This uses event data instead of generating a serial number because -// information can't be attached to the redispatched events, so it has to be -// possible to compute an ID from the identifying data in the event when it is -// received again in order to differentiate between events that are new, and -// events that have been redispatched. -// -// Another alternative would be to compute a checksum from all the data in the -// event (just compute it over the bytes in the struct, probably skipping -// timestamps), but the fields used below are enough to differentiate them, and -// since Windows does some processing on the events (coming up with virtual key -// codes, setting timestamps, etc.), it's not clear that the redispatched -// events would have the same checksums. -uint64_t CalculateEventId(int scancode, int action, bool extended) { - // Calculate a key event ID based on the scan code of the key pressed, - // and the flags we care about. - return scancode | (((action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0) | - (extended ? KEYEVENTF_EXTENDEDKEY : 0x0)) - << 16); -} - -} // namespace - -KeyEventHandler::KeyEventHandler(flutter::BinaryMessenger* messenger, - KeyEventHandler::SendInputDelegate send_input) - : channel_( - std::make_unique>( - messenger, - kChannelName, - &flutter::JsonMessageCodec::GetInstance())), - send_input_(send_input) { - assert(send_input != nullptr); -} - -KeyEventHandler::~KeyEventHandler() = default; - -void KeyEventHandler::TextHook(FlutterWindowsView* view, - const std::u16string& code_point) {} - -KEYBDINPUT* KeyEventHandler::FindPendingEvent(uint64_t id) { - if (pending_events_.empty()) { - return nullptr; - } - for (auto iter = pending_events_.begin(); iter != pending_events_.end(); - ++iter) { - if (iter->first == id) { - return &iter->second; - } - } - return nullptr; -} - -void KeyEventHandler::RemovePendingEvent(uint64_t id) { - for (auto iter = pending_events_.begin(); iter != pending_events_.end(); - ++iter) { - if (iter->first == id) { - pending_events_.erase(iter); - return; - } - } - std::cerr << "Tried to remove pending event with id " << id - << ", but the event was not found." << std::endl; -} - -void KeyEventHandler::AddPendingEvent(uint64_t id, - int scancode, - int action, - bool extended) { - if (pending_events_.size() > kMaxPendingEvents) { - std::cerr - << "There are " << pending_events_.size() - << " keyboard events that have not yet received a response from the " - << "framework. Are responses being sent?" << std::endl; - } - KEYBDINPUT key_event = KEYBDINPUT{0}; - key_event.wScan = scancode; - key_event.dwFlags = KEYEVENTF_SCANCODE | - (extended ? KEYEVENTF_EXTENDEDKEY : 0x0) | - (action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0); - pending_events_.push_back(std::make_pair(id, key_event)); -} - -void KeyEventHandler::HandleResponse(bool handled, - uint64_t id, - int action, - bool extended, - int scancode, - int character) { - if (handled) { - this->RemovePendingEvent(id); - } else { - // Since the framework didn't handle the event, we inject a newly - // synthesized one. We let Windows figure out the virtual key and - // character for the given scancode, as well as a new timestamp. - const KEYBDINPUT* key_event = this->FindPendingEvent(id); - if (key_event == nullptr) { - std::cerr << "Unable to find event " << id << " in pending events queue."; - return; - } - INPUT input_event; - input_event.type = INPUT_KEYBOARD; - input_event.ki = *key_event; - UINT accepted = send_input_(1, &input_event, sizeof(input_event)); - if (accepted != 1) { - std::cerr << "Unable to synthesize event for unhandled keyboard event " - "with scancode " - << scancode << " (character " << character << ")" << std::endl; - } - } -} - -bool KeyEventHandler::KeyboardHook(FlutterWindowsView* view, - int key, - int scancode, - int action, - char32_t character, - bool extended, - bool wasDown) { - const uint64_t id = CalculateEventId(scancode, action, extended); - if (FindPendingEvent(id) != nullptr) { - // Don't pass messages that we synthesized to the framework again. - RemovePendingEvent(id); - return false; - } - - // TODO: Translate to a cross-platform key code system rather than passing - // the native key code. - rapidjson::Document event(rapidjson::kObjectType); - auto& allocator = event.GetAllocator(); - event.AddMember(kKeyCodeKey, key, allocator); - event.AddMember(kScanCodeKey, scancode, allocator); - event.AddMember(kCharacterCodePointKey, character, allocator); - event.AddMember(kKeyMapKey, kWindowsKeyMap, allocator); - event.AddMember(kModifiersKey, GetModsForKeyState(), allocator); - - switch (action) { - case WM_KEYDOWN: - event.AddMember(kTypeKey, kKeyDown, allocator); - break; - case WM_KEYUP: - event.AddMember(kTypeKey, kKeyUp, allocator); - break; - default: - std::cerr << "Unknown key event action: " << action << std::endl; - return false; - } - AddPendingEvent(id, scancode, action, extended); - channel_->Send(event, [this, id, action, extended, scancode, character]( - const uint8_t* reply, size_t reply_size) { - auto decoded = flutter::JsonMessageCodec::GetInstance().DecodeMessage( - reply, reply_size); - bool handled = (*decoded)[kHandledKey].GetBool(); - this->HandleResponse(handled, id, action, extended, scancode, character); - }); - return true; -} - -void KeyEventHandler::ComposeBeginHook() { - // Ignore. -} - -void KeyEventHandler::ComposeEndHook() { - // Ignore. -} - -void KeyEventHandler::ComposeChangeHook(const std::u16string& text, - int cursor_pos) { - // Ignore. -} - -} // namespace flutter diff --git a/shell/platform/windows/keyboard_hook_handler.h b/shell/platform/windows/keyboard_handler_base.h similarity index 94% rename from shell/platform/windows/keyboard_hook_handler.h rename to shell/platform/windows/keyboard_handler_base.h index a8401fdb3e4d3..546af41878e9d 100644 --- a/shell/platform/windows/keyboard_hook_handler.h +++ b/shell/platform/windows/keyboard_handler_base.h @@ -14,9 +14,9 @@ namespace flutter { class FlutterWindowsView; // Abstract class for handling keyboard input events. -class KeyboardHookHandler { +class KeyboardHandlerBase { public: - virtual ~KeyboardHookHandler() = default; + virtual ~KeyboardHandlerBase() = default; // A function for hooking into keyboard input. // @@ -28,7 +28,7 @@ class KeyboardHookHandler { int action, char32_t character, bool extended, - bool wasDown) = 0; + bool was_down) = 0; // A function for hooking into Unicode text input. virtual void TextHook(FlutterWindowsView* view, diff --git a/shell/platform/windows/keyboard_key_channel_handler.cc b/shell/platform/windows/keyboard_key_channel_handler.cc new file mode 100644 index 0000000000000..d70fe68bdbc98 --- /dev/null +++ b/shell/platform/windows/keyboard_key_channel_handler.cc @@ -0,0 +1,140 @@ +// 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/windows/keyboard_key_channel_handler.h" + +#include + +#include + +#include "flutter/shell/platform/common/cpp/json_message_codec.h" + +namespace flutter { + +namespace { + +static constexpr char kChannelName[] = "flutter/keyevent"; + +static constexpr char kKeyCodeKey[] = "keyCode"; +static constexpr char kScanCodeKey[] = "scanCode"; +static constexpr char kCharacterCodePointKey[] = "characterCodePoint"; +static constexpr char kModifiersKey[] = "modifiers"; +static constexpr char kKeyMapKey[] = "keymap"; +static constexpr char kTypeKey[] = "type"; +static constexpr char kHandledKey[] = "handled"; + +static constexpr char kWindowsKeyMap[] = "windows"; +static constexpr char kKeyUp[] = "keyup"; +static constexpr char kKeyDown[] = "keydown"; + +// The maximum number of pending events to keep before +// emitting a warning on the console about unhandled events. +static constexpr int kMaxPendingEvents = 1000; + +// Re-definition of the modifiers for compatibility with the Flutter framework. +// These have to be in sync with the framework's RawKeyEventDataWindows +// modifiers definition. +// https://github.com/flutter/flutter/blob/19ff596979e407c484a32f4071420fca4f4c885f/packages/flutter/lib/src/services/raw_keyboard_windows.dart#L203 +static constexpr int kShift = 1 << 0; +static constexpr int kShiftLeft = 1 << 1; +static constexpr int kShiftRight = 1 << 2; +static constexpr int kControl = 1 << 3; +static constexpr int kControlLeft = 1 << 4; +static constexpr int kControlRight = 1 << 5; +static constexpr int kAlt = 1 << 6; +static constexpr int kAltLeft = 1 << 7; +static constexpr int kAltRight = 1 << 8; +static constexpr int kWinLeft = 1 << 9; +static constexpr int kWinRight = 1 << 10; +static constexpr int kCapsLock = 1 << 11; +static constexpr int kNumLock = 1 << 12; +static constexpr int kScrollLock = 1 << 13; + +/// Calls GetKeyState() an all modifier keys and packs the result in an int, +/// with the re-defined values declared above for compatibility with the Flutter +/// framework. +int GetModsForKeyState() { + int mods = 0; + + if (GetKeyState(VK_SHIFT) < 0) + mods |= kShift; + if (GetKeyState(VK_LSHIFT) < 0) + mods |= kShiftLeft; + if (GetKeyState(VK_RSHIFT) < 0) + mods |= kShiftRight; + if (GetKeyState(VK_CONTROL) < 0) + mods |= kControl; + if (GetKeyState(VK_LCONTROL) < 0) + mods |= kControlLeft; + if (GetKeyState(VK_RCONTROL) < 0) + mods |= kControlRight; + if (GetKeyState(VK_MENU) < 0) + mods |= kAlt; + if (GetKeyState(VK_LMENU) < 0) + mods |= kAltLeft; + if (GetKeyState(VK_RMENU) < 0) + mods |= kAltRight; + if (GetKeyState(VK_LWIN) < 0) + mods |= kWinLeft; + if (GetKeyState(VK_RWIN) < 0) + mods |= kWinRight; + if (GetKeyState(VK_CAPITAL) < 0) + mods |= kCapsLock; + if (GetKeyState(VK_NUMLOCK) < 0) + mods |= kNumLock; + if (GetKeyState(VK_SCROLL) < 0) + mods |= kScrollLock; + return mods; +} + +} // namespace + +KeyboardKeyChannelHandler::KeyboardKeyChannelHandler( + flutter::BinaryMessenger* messenger) + : channel_( + std::make_unique>( + messenger, + kChannelName, + &flutter::JsonMessageCodec::GetInstance())) {} + +KeyboardKeyChannelHandler::~KeyboardKeyChannelHandler() = default; + +void KeyboardKeyChannelHandler::KeyboardHook(int key, + int scancode, + int action, + char32_t character, + bool extended, + bool was_down, + std::function callback) { + // TODO: Translate to a cross-platform key code system rather than passing + // the native key code. + rapidjson::Document event(rapidjson::kObjectType); + auto& allocator = event.GetAllocator(); + event.AddMember(kKeyCodeKey, key, allocator); + event.AddMember(kScanCodeKey, scancode, allocator); + event.AddMember(kCharacterCodePointKey, character, allocator); + event.AddMember(kKeyMapKey, kWindowsKeyMap, allocator); + event.AddMember(kModifiersKey, GetModsForKeyState(), allocator); + + switch (action) { + case WM_KEYDOWN: + event.AddMember(kTypeKey, kKeyDown, allocator); + break; + case WM_KEYUP: + event.AddMember(kTypeKey, kKeyUp, allocator); + break; + default: + std::cerr << "Unknown key event action: " << action << std::endl; + return; + } + channel_->Send(event, [callback = std::move(callback)]( + const uint8_t* reply, size_t reply_size) { + auto decoded = flutter::JsonMessageCodec::GetInstance().DecodeMessage( + reply, reply_size); + bool handled = (*decoded)[kHandledKey].GetBool(); + callback(handled); + }); +} + +} // namespace flutter diff --git a/shell/platform/windows/keyboard_key_channel_handler.h b/shell/platform/windows/keyboard_key_channel_handler.h new file mode 100644 index 0000000000000..255b6ab22e8b8 --- /dev/null +++ b/shell/platform/windows/keyboard_key_channel_handler.h @@ -0,0 +1,45 @@ +// 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_WINDOWS_KEYBOARD_KEY_CHANNEL_HANDLER_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_KEY_CHANNEL_HANDLER_H_ + +#include +#include +#include + +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h" +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h" +#include "flutter/shell/platform/windows/keyboard_key_handler.h" +#include "flutter/shell/platform/windows/public/flutter_windows.h" +#include "rapidjson/document.h" + +namespace flutter { + +class FlutterWindowsView; + +class KeyboardKeyChannelHandler + : public KeyboardKeyHandler::KeyboardKeyHandlerDelegate { + public: + explicit KeyboardKeyChannelHandler(flutter::BinaryMessenger* messenger); + + ~KeyboardKeyChannelHandler(); + + // |KeyboardKeyHandler::KeyboardKeyHandlerDelegate| + void KeyboardHook(int key, + int scancode, + int action, + char32_t character, + bool extended, + bool was_down, + std::function callback); + + private: + // The Flutter system channel for key event messages. + std::unique_ptr> channel_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_KEY_CHANNEL_HANDLER_H_ diff --git a/shell/platform/windows/key_event_handler_unittests.cc b/shell/platform/windows/keyboard_key_channel_handler_unittests.cc similarity index 97% rename from shell/platform/windows/key_event_handler_unittests.cc rename to shell/platform/windows/keyboard_key_channel_handler_unittests.cc index 398afc5efc68a..9eb3bad94fda2 100644 --- a/shell/platform/windows/key_event_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_channel_handler_unittests.cc @@ -1,118 +1,118 @@ -// 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/windows/key_event_handler.h" - -#include -#include - -#include "flutter/shell/platform/common/cpp/json_message_codec.h" -#include "flutter/shell/platform/windows/flutter_windows_view.h" -#include "flutter/shell/platform/windows/testing/test_binary_messenger.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -namespace flutter { -namespace testing { - -static constexpr char kScanCodeKey[] = "scanCode"; -static constexpr int kHandledScanCode = 20; -static constexpr int kUnhandledScanCode = 21; - -std::unique_ptr> CreateResponse(bool handled) { - auto response_doc = - std::make_unique(rapidjson::kObjectType); - auto& allocator = response_doc->GetAllocator(); - response_doc->AddMember("handled", handled, allocator); - return JsonMessageCodec::GetInstance().EncodeMessage(*response_doc); -} - -TEST(KeyEventHandlerTest, KeyboardHookHandling) { - auto handled_message = CreateResponse(true); - auto unhandled_message = CreateResponse(false); - int received_scancode = 0; - - TestBinaryMessenger messenger( - [&received_scancode, &handled_message, &unhandled_message]( - const std::string& channel, const uint8_t* message, - size_t message_size, BinaryReply reply) { - if (channel == "flutter/keyevent") { - auto message_doc = JsonMessageCodec::GetInstance().DecodeMessage( - message, message_size); - received_scancode = (*message_doc)[kScanCodeKey].GetInt(); - if (received_scancode == kHandledScanCode) { - reply(handled_message->data(), handled_message->size()); - } else { - reply(unhandled_message->data(), unhandled_message->size()); - } - } - }); - - int redispatch_scancode = 0; - KeyEventHandler handler(&messenger, - [&redispatch_scancode](UINT cInputs, LPINPUT pInputs, - int cbSize) -> UINT { - EXPECT_TRUE(cbSize > 0); - redispatch_scancode = pInputs->ki.wScan; - return 1; - }); - - handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, L'a', false); - EXPECT_EQ(received_scancode, kHandledScanCode); - EXPECT_EQ(redispatch_scancode, 0); - received_scancode = 0; - handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', - false); - EXPECT_EQ(received_scancode, kUnhandledScanCode); - EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); -} - -TEST(KeyEventHandlerTest, ExtendedKeysAreSentToRedispatch) { - auto handled_message = CreateResponse(true); - auto unhandled_message = CreateResponse(false); - int received_scancode = 0; - bool is_extended_key = false; - - TestBinaryMessenger messenger( - [&received_scancode, &handled_message, &unhandled_message]( - const std::string& channel, const uint8_t* message, - size_t message_size, BinaryReply reply) { - if (channel == "flutter/keyevent") { - auto message_doc = JsonMessageCodec::GetInstance().DecodeMessage( - message, message_size); - received_scancode = (*message_doc)[kScanCodeKey].GetInt(); - if (received_scancode == kHandledScanCode) { - reply(handled_message->data(), handled_message->size()); - } else { - reply(unhandled_message->data(), unhandled_message->size()); - } - } - }); - - int redispatch_scancode = 0; - KeyEventHandler handler( - &messenger, - [&redispatch_scancode, &is_extended_key](UINT cInputs, LPINPUT pInputs, - int cbSize) -> UINT { - EXPECT_TRUE(cbSize > 0); - redispatch_scancode = pInputs->ki.wScan; - is_extended_key = (pInputs->ki.dwFlags & KEYEVENTF_EXTENDEDKEY) != 0; - return 1; - }); - - // Extended key flag is passed to redispatched events if set. - handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', true); - EXPECT_EQ(received_scancode, kUnhandledScanCode); - EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); - EXPECT_EQ(is_extended_key, true); - - // Extended key flag is not passed to redispatched events if not set. - handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', - false); - EXPECT_EQ(received_scancode, kUnhandledScanCode); - EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); - EXPECT_EQ(is_extended_key, false); -} - -} // namespace testing -} // namespace flutter +// 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/windows/key_event_handler.h" + +#include +#include + +#include "flutter/shell/platform/common/cpp/json_message_codec.h" +#include "flutter/shell/platform/windows/flutter_windows_view.h" +#include "flutter/shell/platform/windows/testing/test_binary_messenger.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +static constexpr char kScanCodeKey[] = "scanCode"; +static constexpr int kHandledScanCode = 20; +static constexpr int kUnhandledScanCode = 21; + +std::unique_ptr> CreateResponse(bool handled) { + auto response_doc = + std::make_unique(rapidjson::kObjectType); + auto& allocator = response_doc->GetAllocator(); + response_doc->AddMember("handled", handled, allocator); + return JsonMessageCodec::GetInstance().EncodeMessage(*response_doc); +} + +TEST(KeyEventHandlerTest, KeyboardHookHandling) { + auto handled_message = CreateResponse(true); + auto unhandled_message = CreateResponse(false); + int received_scancode = 0; + + TestBinaryMessenger messenger( + [&received_scancode, &handled_message, &unhandled_message]( + const std::string& channel, const uint8_t* message, + size_t message_size, BinaryReply reply) { + if (channel == "flutter/keyevent") { + auto message_doc = JsonMessageCodec::GetInstance().DecodeMessage( + message, message_size); + received_scancode = (*message_doc)[kScanCodeKey].GetInt(); + if (received_scancode == kHandledScanCode) { + reply(handled_message->data(), handled_message->size()); + } else { + reply(unhandled_message->data(), unhandled_message->size()); + } + } + }); + + int redispatch_scancode = 0; + KeyEventHandler handler(&messenger, + [&redispatch_scancode](UINT cInputs, LPINPUT pInputs, + int cbSize) -> UINT { + EXPECT_TRUE(cbSize > 0); + redispatch_scancode = pInputs->ki.wScan; + return 1; + }); + + handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, L'a', false); + EXPECT_EQ(received_scancode, kHandledScanCode); + EXPECT_EQ(redispatch_scancode, 0); + received_scancode = 0; + handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', + false); + EXPECT_EQ(received_scancode, kUnhandledScanCode); + EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); +} + +TEST(KeyEventHandlerTest, ExtendedKeysAreSentToRedispatch) { + auto handled_message = CreateResponse(true); + auto unhandled_message = CreateResponse(false); + int received_scancode = 0; + bool is_extended_key = false; + + TestBinaryMessenger messenger( + [&received_scancode, &handled_message, &unhandled_message]( + const std::string& channel, const uint8_t* message, + size_t message_size, BinaryReply reply) { + if (channel == "flutter/keyevent") { + auto message_doc = JsonMessageCodec::GetInstance().DecodeMessage( + message, message_size); + received_scancode = (*message_doc)[kScanCodeKey].GetInt(); + if (received_scancode == kHandledScanCode) { + reply(handled_message->data(), handled_message->size()); + } else { + reply(unhandled_message->data(), unhandled_message->size()); + } + } + }); + + int redispatch_scancode = 0; + KeyEventHandler handler( + &messenger, + [&redispatch_scancode, &is_extended_key](UINT cInputs, LPINPUT pInputs, + int cbSize) -> UINT { + EXPECT_TRUE(cbSize > 0); + redispatch_scancode = pInputs->ki.wScan; + is_extended_key = (pInputs->ki.dwFlags & KEYEVENTF_EXTENDEDKEY) != 0; + return 1; + }); + + // Extended key flag is passed to redispatched events if set. + handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', true); + EXPECT_EQ(received_scancode, kUnhandledScanCode); + EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); + EXPECT_EQ(is_extended_key, true); + + // Extended key flag is not passed to redispatched events if not set. + handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', + false); + EXPECT_EQ(received_scancode, kUnhandledScanCode); + EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); + EXPECT_EQ(is_extended_key, false); +} + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/windows/flutter_keyboard_manager.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc similarity index 70% rename from shell/platform/windows/flutter_keyboard_manager.cc rename to shell/platform/windows/keyboard_key_embedder_handler.cc index 9cc1e59a2c4c6..9557847cfecf4 100644 --- a/shell/platform/windows/flutter_keyboard_manager.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/shell/platform/windows/flutter_keyboard_manager.h" +#include "flutter/shell/platform/windows/keyboard_key_embedder_handler.h" #include #include @@ -45,14 +45,13 @@ constexpr uint64_t kWindowsKeyIdPlane = 0x00700000000; */ constexpr uint64_t kAutogeneratedMask = 0x10000000000; -FlutterKeyboardManager::FlutterKeyboardManager( - std::function onEvent) - : onEvent_(onEvent) {} +FlutterKeyboardKeyEmbedderHandler::FlutterKeyboardKeyEmbedderHandler( + std::function send_event) + : sendEvent_(send_event), response_id_(1) {} -FlutterKeyboardManager::~FlutterKeyboardManager() = default; - -void FlutterKeyboardManager::TextHook(FlutterWindowsView* view, - const std::u16string& code_point) {} +FlutterKeyboardKeyEmbedderHandler::~FlutterKeyboardKeyEmbedderHandler() = default; static bool isAsciiPrintable(int codeUnit) { return codeUnit <= 0x7f && codeUnit >= 0x20; @@ -64,27 +63,25 @@ static bool isControlCharacter(int codeUnit) { } // Transform scancodes sent by windows to scancodes written in Chromium spec. -static uint16_t normalizeScancode(int windowsScanCode) { - // Windows scancode is composed of 1 bit of "extended" and 8 bits of code, - // concatenated (by Flutter embedding), i.e. PageUp is represented as 0x149. - // In Chromium spec the extended bit is shown as 0xe000 bit, making PageUp - // 0xe049. - return (windowsScanCode & 0xff) | ((windowsScanCode & 0x100) ? 0xe000 : 0); +static uint16_t normalizeScancode(int windowsScanCode, bool extended) { + // In Chromium spec the extended bit is shown as 0xe000 bit, + // i.e. PageUp is represented as 0xe049. + return (windowsScanCode & 0xff) | (extended ? 0xe000 : 0); } -uint64_t FlutterKeyboardManager::getPhysicalKey(int scancode) { - int chromiumScancode = normalizeScancode(scancode); +uint64_t FlutterKeyboardKeyEmbedderHandler::getPhysicalKey(int scancode, bool extended) { + int chromiumScancode = normalizeScancode(scancode, extended); auto resultIt = windowsToPhysicalMap_.find(chromiumScancode); if (resultIt != windowsToPhysicalMap_.end()) return resultIt->second; return scancode | kHidPlane; } -uint64_t FlutterKeyboardManager::getLogicalKey(int key, int scancode) { +uint64_t FlutterKeyboardKeyEmbedderHandler::getLogicalKey(int key, bool extended, int scancode) { // Normally logical keys should only be derived from key codes, but since some // key codes are either 0 or ambiguous (multiple keys using the same key // code), these keys are resolved by scan codes. - auto numpadIt = scanCodeToLogicalMap_.find(normalizeScancode(scancode)); + auto numpadIt = scanCodeToLogicalMap_.find(normalizeScancode(scancode, extended)); if (numpadIt != scanCodeToLogicalMap_.cend()) return numpadIt->second; @@ -114,7 +111,7 @@ uint64_t FlutterKeyboardManager::getLogicalKey(int key, int scancode) { } } -void FlutterKeyboardManager::cacheUtf8String(char32_t character) { +void FlutterKeyboardKeyEmbedderHandler::cacheUtf8String(char32_t character) { if (character == 0) { character_cache_[0] = '\0'; return; @@ -124,14 +121,16 @@ void FlutterKeyboardManager::cacheUtf8String(char32_t character) { strcpy_s(character_cache_, kCharacterCacheSize, Utf8FromUtf16(text).c_str()); } -void FlutterKeyboardManager::KeyboardHook(FlutterWindowsView* view, - int key, - int scancode, - int action, - char32_t character, - bool was_down) { - const uint64_t physical_key = getPhysicalKey(scancode); - const uint64_t logical_key = getLogicalKey(key, scancode); +void FlutterKeyboardKeyEmbedderHandler::KeyboardHook( + int key, + int scancode, + int action, + char32_t character, + bool extended, + bool was_down, + std::function callback) { + const uint64_t physical_key = getPhysicalKey(scancode, extended); + const uint64_t logical_key = getLogicalKey(key, extended, scancode); assert(action == WM_KEYDOWN || action == WM_KEYUP); const bool is_physical_down = action == WM_KEYDOWN; @@ -140,8 +139,8 @@ void FlutterKeyboardManager::KeyboardHook(FlutterWindowsView* view, const uint64_t last_logical_record = had_record ? last_logical_record_iter->second : 0; - // The resulting event's `change`. - FlutterKeyEventKind change; + // The resulting event's `type`. + FlutterKeyEventType type; // The resulting event's `logical_key`. uint64_t result_logical_key; // The next value of pressingRecords_[physical_key] (or to remove it). @@ -152,7 +151,7 @@ void FlutterKeyboardManager::KeyboardHook(FlutterWindowsView* view, if (had_record) { if (was_down) { // A normal repeated key. - change = kFlutterKeyEventKindRepeat; + type = kFlutterKeyEventTypeRepeat; assert(had_record); cacheUtf8String(character); next_logical_record = last_logical_record; @@ -166,7 +165,7 @@ void FlutterKeyboardManager::KeyboardHook(FlutterWindowsView* view, } } else { // A normal down event (whether the system event is a repeat or not). - change = kFlutterKeyEventKindDown; + type = kFlutterKeyEventTypeDown; assert(!had_record); cacheUtf8String(character); next_logical_record = logical_key; @@ -179,7 +178,7 @@ void FlutterKeyboardManager::KeyboardHook(FlutterWindowsView* view, return; } else { // A normal up event. - change = kFlutterKeyEventKindUp; + type = kFlutterKeyEventTypeUp; assert(had_record); // Up events never have character. character_cache_[0] = '\0'; @@ -208,12 +207,37 @@ void FlutterKeyboardManager::KeyboardHook(FlutterWindowsView* view, std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch()) .count(); - key_data.kind = change; + key_data.type = type; key_data.physical = physical_key; key_data.logical = result_logical_key; key_data.character = character_cache_; key_data.synthesized = false; - onEvent_(key_data); + + response_id_ += 1; + uint64_t response_id = response_id_; + PendingResponse pending{ + .callback = [this, callback = std::move(callback)] + (bool handled, uint64_t reponse_id) { + auto found = pending_responses_.find(reponse_id); + if (found != pending_responses_.end()) { + pending_responses_.erase(found); + } + callback(handled); + }, + .response_id = response_id, + }; + auto pending_ptr = std::make_unique(std::move(pending)); + pending_responses_[response_id] = std::move(pending_ptr); + sendEvent_(key_data, + FlutterKeyboardKeyEmbedderHandler::HandleResponse, + reinterpret_cast(pending_responses_[response_id].get())); +} + +void FlutterKeyboardKeyEmbedderHandler::HandleResponse( + bool handled, void* user_data) { + PendingResponse* pending = reinterpret_cast(user_data); + auto callback = std::move(pending->callback); + callback(handled, pending->response_id); } } // namespace flutter diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h new file mode 100644 index 0000000000000..3c6bd6d2777c3 --- /dev/null +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -0,0 +1,83 @@ +// 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_WINDOWS_KEYBOARD_KEY_EMBEDDER_HANDLER_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_KEY_EMBEDDER_HANDLER_H_ + +#include +#include +#include +#include + +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/windows/keyboard_key_handler.h" +#include "flutter/shell/platform/windows/public/flutter_windows.h" + +namespace flutter { + +namespace { +// An arbitrary size for the character cache in bytes. +// +// It should hold a UTF-32 character encoded in UTF-8 as long as the trailing +// '\0'. +constexpr size_t kCharacterCacheSize = 8; +} // namespace + +// Processes native keyboard events. +// +// This class converts received keyboard events to Flutter events, +// sends them to the engine, waits for response. +class FlutterKeyboardKeyEmbedderHandler + : public KeyboardKeyHandler::KeyboardKeyHandlerDelegate { + public: + // Build a FlutterKeyboardKeyEmbedderHandler. + // + // Use `send_event` to define how the manager should dispatch converted + // flutter events, as well as how to receive the resopnse, to the engine. It's + // typically FlutterWindowsEngine::SendKeyEvent. + explicit FlutterKeyboardKeyEmbedderHandler( + std::function send_event); + + virtual ~FlutterKeyboardKeyEmbedderHandler(); + + // |KeyboardHandlerBase| + void KeyboardHook(int key, + int scancode, + int action, + char32_t character, + bool extended, + bool was_down, + std::function callback) override; + + private: + struct PendingResponse { + std::function callback; + uint64_t response_id; + }; + + void cacheUtf8String(char32_t ch); + + std::function sendEvent_; + std::map pressingRecords_; + char character_cache_[kCharacterCacheSize]; + std::map> pending_responses_; + uint64_t response_id_; + + static uint64_t getPhysicalKey(int scancode, bool extended); + static uint64_t getLogicalKey(int key, bool extended, int scancode); + + static std::map windowsToPhysicalMap_; + static std::map windowsToLogicalMap_; + static std::map scanCodeToLogicalMap_; + + static void HandleResponse(bool handled, void* user_data); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_KEY_EMBEDDER_HANDLER_H_ diff --git a/shell/platform/windows/flutter_keyboard_manager_unittests.cc b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc similarity index 83% rename from shell/platform/windows/flutter_keyboard_manager_unittests.cc rename to shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index 4c70cf6f909f3..9e53b3659f30e 100644 --- a/shell/platform/windows/flutter_keyboard_manager_unittests.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/shell/platform/windows/flutter_keyboard_manager.h" +#include "flutter/shell/platform/windows/keyboard_key_embedder_handler.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" @@ -54,23 +54,24 @@ constexpr uint64_t kLogicalNumpadEnd = 0x23; constexpr uint64_t kLogicalNumLock = 0x90; } // namespace -TEST(FlutterKeyboardManager, BasicKeyPressingAndHolding) { +// Test the most basic key events. +TEST(FlutterKeyboardKeyEmbedderHandler, BasicKeyPressingAndHolding) { std::vector results; TestFlutterKeyEvent* event; - std::unique_ptr manager = - std::make_unique( + std::unique_ptr manager = + std::make_unique( [&results](const FlutterKeyEvent& event) { results.emplace_back(event); }); // On a US keyboard: - // Press Numpad1. + // Press KeyA. manager->KeyboardHook(nullptr, kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', false); EXPECT_EQ(results.size(), 1); event = &results[0]; - EXPECT_EQ(event->kind, kFlutterKeyEventKindDown); + EXPECT_EQ(event->kind, kFlutterKeyEventTypeDown); EXPECT_EQ(event->physical, 0x00070004); EXPECT_EQ(event->logical, 0x00000061); EXPECT_STREQ(event->character, "a"); @@ -82,7 +83,7 @@ TEST(FlutterKeyboardManager, BasicKeyPressingAndHolding) { true); EXPECT_EQ(results.size(), 1); event = &results[0]; - EXPECT_EQ(event->kind, kFlutterKeyEventKindRepeat); + EXPECT_EQ(event->kind, kFlutterKeyEventTypeRepeat); EXPECT_EQ(event->physical, 0x00070004); EXPECT_EQ(event->logical, 0x00000061); EXPECT_STREQ(event->character, "a"); @@ -94,19 +95,19 @@ TEST(FlutterKeyboardManager, BasicKeyPressingAndHolding) { true); EXPECT_EQ(results.size(), 1); event = &results[0]; - EXPECT_EQ(event->kind, kFlutterKeyEventKindUp); + EXPECT_EQ(event->kind, kFlutterKeyEventTypeUp); EXPECT_EQ(event->physical, 0x00070004); EXPECT_EQ(event->logical, 0x00000061); EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, false); } -TEST(FlutterKeyboardManager, ToggleNumLockDuringNumpadPress) { +TEST(FlutterKeyboardKeyEmbedderHandler, ToggleNumLockDuringNumpadPress) { std::vector results; TestFlutterKeyEvent* event; - std::unique_ptr manager = - std::make_unique( + std::unique_ptr manager = + std::make_unique( [&results](const FlutterKeyEvent& event) { results.emplace_back(event); }); @@ -117,7 +118,7 @@ TEST(FlutterKeyboardManager, ToggleNumLockDuringNumpadPress) { 0, false); EXPECT_EQ(results.size(), 1); event = &results[0]; - EXPECT_EQ(event->kind, kFlutterKeyEventKindDown); + EXPECT_EQ(event->kind, kFlutterKeyEventTypeDown); EXPECT_EQ(event->physical, 0x00070059); EXPECT_EQ(event->logical, 0x00200000031); // EXPECT_STREQ(event->character, "1"); // TODO @@ -129,7 +130,7 @@ TEST(FlutterKeyboardManager, ToggleNumLockDuringNumpadPress) { 0, false); EXPECT_EQ(results.size(), 1); event = &results[0]; - EXPECT_EQ(event->kind, kFlutterKeyEventKindDown); + EXPECT_EQ(event->kind, kFlutterKeyEventTypeDown); EXPECT_EQ(event->physical, 0x00070053); EXPECT_EQ(event->logical, 0x0000010a); EXPECT_STREQ(event->character, ""); @@ -141,7 +142,7 @@ TEST(FlutterKeyboardManager, ToggleNumLockDuringNumpadPress) { false); EXPECT_EQ(results.size(), 1); event = &results[0]; - EXPECT_EQ(event->kind, kFlutterKeyEventKindUp); + EXPECT_EQ(event->kind, kFlutterKeyEventTypeUp); EXPECT_EQ(event->physical, 0x00070053); EXPECT_EQ(event->logical, 0x0000010a); EXPECT_STREQ(event->character, ""); @@ -153,7 +154,7 @@ TEST(FlutterKeyboardManager, ToggleNumLockDuringNumpadPress) { 0, false); EXPECT_EQ(results.size(), 1); event = &results[0]; - EXPECT_EQ(event->kind, kFlutterKeyEventKindUp); + EXPECT_EQ(event->kind, kFlutterKeyEventTypeUp); EXPECT_EQ(event->physical, 0x00070059); EXPECT_EQ(event->logical, 0x00200000031); EXPECT_STREQ(event->character, ""); diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc new file mode 100644 index 0000000000000..7eaa0d6fa4516 --- /dev/null +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -0,0 +1,173 @@ +// 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/windows/keyboard_key_handler.h" + +#include + +#include + +#include "flutter/shell/platform/common/cpp/json_message_codec.h" + +namespace flutter { + +namespace { + +static constexpr char kChannelName[] = "flutter/keyevent"; + +static constexpr char kKeyCodeKey[] = "keyCode"; +static constexpr char kScanCodeKey[] = "scanCode"; +static constexpr char kCharacterCodePointKey[] = "characterCodePoint"; +static constexpr char kModifiersKey[] = "modifiers"; +static constexpr char kKeyMapKey[] = "keymap"; +static constexpr char kTypeKey[] = "type"; +static constexpr char kHandledKey[] = "handled"; + +static constexpr char kWindowsKeyMap[] = "windows"; +static constexpr char kKeyUp[] = "keyup"; +static constexpr char kKeyDown[] = "keydown"; + +// The maximum number of pending events to keep before +// emitting a warning on the console about unhandled events. +static constexpr int kMaxPendingEvents = 1000; + +// This uses event data instead of generating a serial number because +// information can't be attached to the redispatched events, so it has to be +// possible to compute an ID from the identifying data in the event when it is +// received again in order to differentiate between events that are new, and +// events that have been redispatched. +// +// Another alternative would be to compute a checksum from all the data in the +// event (just compute it over the bytes in the struct, probably skipping +// timestamps), but the fields used below are enough to differentiate them, and +// since Windows does some processing on the events (coming up with virtual key +// codes, setting timestamps, etc.), it's not clear that the redispatched +// events would have the same checksums. +uint64_t CalculateEventId(int scancode, int action, bool extended) { + // Calculate a key event ID based on the scan code of the key pressed, + // and the flags we care about. + return scancode | (((action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0) | + (extended ? KEYEVENTF_EXTENDEDKEY : 0x0)) + << 16); +} + +} // namespace + +KeyboardKeyHandler::KeyboardKeyHandlerDelegate::~KeyboardKeyHandlerDelegate() = default; + +KeyboardKeyHandler::KeyboardKeyHandler(SendInputDelegate send_input) + : send_input_(send_input) { + assert(send_input != nullptr); +} + +KeyboardKeyHandler::~KeyboardKeyHandler() = default; + +void KeyboardKeyHandler::TextHook(FlutterWindowsView* view, + const std::u16string& code_point) {} + +void KeyboardKeyHandler::AddDelegate( + std::unique_ptr delegate) { + delegates_.push_back(std::move(delegate)); +} + +const KeyboardKeyHandler::PendingEvent* KeyboardKeyHandler::FindPendingEvent(uint64_t id) { + if (pending_events_.empty()) { + return nullptr; + } + for (auto iter = pending_events_.begin(); iter != pending_events_.end(); + ++iter) { + if ((*iter)->id == id) { + return iter->get(); + } + } + return nullptr; +} + +void KeyboardKeyHandler::RemovePendingEvent(uint64_t id) { + for (auto iter = pending_events_.begin(); iter != pending_events_.end(); + ++iter) { + if ((*iter)->id == id) { + pending_events_.erase(iter); + return; + } + } + std::cerr << "Tried to remove pending event with id " << id + << ", but the event was not found." << std::endl; +} + +void KeyboardKeyHandler::RedispatchEvent(const PendingEvent* pending) { + KEYBDINPUT ki{ + .wVk = 0, + .wScan = static_cast(pending->scancode), + .dwFlags = pending->dwFlags, + }; + INPUT input_event; + input_event.type = INPUT_KEYBOARD; + input_event.ki = std::move(ki); + UINT accepted = send_input_(1, &input_event, sizeof(input_event)); + if (accepted != 1) { + std::cerr << "Unable to synthesize event for unhandled keyboard event " + "with scancode " + << pending->scancode << " (character " << pending->character << ")" << std::endl; + } +} + +bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, + int key, + int scancode, + int action, + char32_t character, + bool extended, + bool was_down) { + const uint64_t id = CalculateEventId(scancode, action, extended); + if (FindPendingEvent(id) != nullptr) { + // Don't pass messages that we synthesized to the framework again. + RemovePendingEvent(id); + return false; + } + + if (pending_events_.size() > kMaxPendingEvents) { + std::cerr + << "There are " << pending_events_.size() + << " keyboard events that have not yet received a response from the " + << "framework. Are responses being sent?" << std::endl; + } + PendingEvent pending{ + .id = id, + .scancode = scancode, + .character = character, + .dwFlags = static_cast(KEYEVENTF_SCANCODE | + (extended ? KEYEVENTF_EXTENDEDKEY : 0x0) | + (action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0)), + .unreplied = delegates_.size(), + }; + pending_events_.push_back(std::make_unique(std::move(pending))); + + PendingEvent& pending_event = *pending_events_.back(); + for (const auto& delegate : delegates_) { + delegate->KeyboardHook(key, scancode, action, character, extended, + was_down, [pending_event = &pending_event, this](bool handled) { + pending_event->unreplied -= 1; + if (pending_event->unreplied == 0) { + RedispatchEvent(pending_event); + } + }); + } + return true; +} + +void KeyboardKeyHandler::ComposeBeginHook() { + // Ignore. +} + +void KeyboardKeyHandler::ComposeEndHook() { + // Ignore. +} + +void KeyboardKeyHandler::ComposeChangeHook(const std::u16string& text, + int cursor_pos) { + // Ignore. +} + +} // namespace flutter diff --git a/shell/platform/windows/key_event_handler.h b/shell/platform/windows/keyboard_key_handler.h similarity index 55% rename from shell/platform/windows/key_event_handler.h rename to shell/platform/windows/keyboard_key_handler.h index c45e4ee873745..b78e135f6fed5 100644 --- a/shell/platform/windows/key_event_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_KEY_EVENT_HANDLER_H_ -#define FLUTTER_SHELL_PLATFORM_WINDOWS_KEY_EVENT_HANDLER_H_ +#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_KEY_HANDLER_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_KEY_HANDLER_H_ #include #include @@ -11,7 +11,7 @@ #include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h" #include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h" -#include "flutter/shell/platform/windows/keyboard_hook_handler.h" +#include "flutter/shell/platform/windows/keyboard_handler_base.h" #include "flutter/shell/platform/windows/public/flutter_windows.h" #include "rapidjson/document.h" @@ -19,43 +19,65 @@ namespace flutter { class FlutterWindowsView; -// Implements a KeyboardHookHandler +// Implements a KeyboardHandlerBase // // Handles key events and forwards them to the Flutter engine. -class KeyEventHandler : public KeyboardHookHandler { +class KeyboardKeyHandler : public KeyboardHandlerBase { public: + class KeyboardKeyHandlerDelegate { + public: + virtual void KeyboardHook(int key, + int scancode, + int action, + char32_t character, + bool extended, + bool was_down, + std::function callback) = 0; + + virtual ~KeyboardKeyHandlerDelegate(); + }; + using SendInputDelegate = std::function; - explicit KeyEventHandler(flutter::BinaryMessenger* messenger, - SendInputDelegate delegate = SendInput); + explicit KeyboardKeyHandler(SendInputDelegate delegate = SendInput); + + ~KeyboardKeyHandler(); - virtual ~KeyEventHandler(); + void AddDelegate(std::unique_ptr delegate); - // |KeyboardHookHandler| + // |KeyboardHandlerBase| bool KeyboardHook(FlutterWindowsView* window, int key, int scancode, int action, char32_t character, bool extended, - bool wasDown) override; + bool was_down) override; - // |KeyboardHookHandler| + // |KeyboardHandlerBase| void TextHook(FlutterWindowsView* window, const std::u16string& text) override; - // |KeyboardHookHandler| + // |KeyboardHandlerBase| void ComposeBeginHook() override; - // |KeyboardHookHandler| + // |KeyboardHandlerBase| void ComposeEndHook() override; - // |KeyboardHookHandler| + // |KeyboardHandlerBase| void ComposeChangeHook(const std::u16string& text, int cursor_pos) override; private: - KEYBDINPUT* FindPendingEvent(uint64_t id); + struct PendingEvent { + uint64_t id; + int scancode; + char32_t character; + DWORD dwFlags; + size_t unreplied; + }; + + const PendingEvent* FindPendingEvent(uint64_t id); void RemovePendingEvent(uint64_t id); void AddPendingEvent(uint64_t id, int scancode, int action, bool extended); void HandleResponse(bool handled, @@ -64,13 +86,13 @@ class KeyEventHandler : public KeyboardHookHandler { bool extended, int scancode, int character); + void RedispatchEvent(const PendingEvent* pending); - // The Flutter system channel for key event messages. - std::unique_ptr> channel_; + std::vector> delegates_; // The queue of key events that have been sent to the framework but have not // yet received a response. - std::deque> pending_events_; + std::deque> pending_events_; // A function used to dispatch synthesized events. Used in testing to inject a // test function to collect events. Defaults to the Windows function @@ -80,4 +102,4 @@ class KeyEventHandler : public KeyboardHookHandler { } // namespace flutter -#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_KEY_EVENT_HANDLER_H_ +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_KEY_HANDLER_H_ diff --git a/shell/platform/windows/keyboard_key_handler_unittests.cc b/shell/platform/windows/keyboard_key_handler_unittests.cc new file mode 100644 index 0000000000000..9eb3bad94fda2 --- /dev/null +++ b/shell/platform/windows/keyboard_key_handler_unittests.cc @@ -0,0 +1,118 @@ +// 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/windows/key_event_handler.h" + +#include +#include + +#include "flutter/shell/platform/common/cpp/json_message_codec.h" +#include "flutter/shell/platform/windows/flutter_windows_view.h" +#include "flutter/shell/platform/windows/testing/test_binary_messenger.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +static constexpr char kScanCodeKey[] = "scanCode"; +static constexpr int kHandledScanCode = 20; +static constexpr int kUnhandledScanCode = 21; + +std::unique_ptr> CreateResponse(bool handled) { + auto response_doc = + std::make_unique(rapidjson::kObjectType); + auto& allocator = response_doc->GetAllocator(); + response_doc->AddMember("handled", handled, allocator); + return JsonMessageCodec::GetInstance().EncodeMessage(*response_doc); +} + +TEST(KeyEventHandlerTest, KeyboardHookHandling) { + auto handled_message = CreateResponse(true); + auto unhandled_message = CreateResponse(false); + int received_scancode = 0; + + TestBinaryMessenger messenger( + [&received_scancode, &handled_message, &unhandled_message]( + const std::string& channel, const uint8_t* message, + size_t message_size, BinaryReply reply) { + if (channel == "flutter/keyevent") { + auto message_doc = JsonMessageCodec::GetInstance().DecodeMessage( + message, message_size); + received_scancode = (*message_doc)[kScanCodeKey].GetInt(); + if (received_scancode == kHandledScanCode) { + reply(handled_message->data(), handled_message->size()); + } else { + reply(unhandled_message->data(), unhandled_message->size()); + } + } + }); + + int redispatch_scancode = 0; + KeyEventHandler handler(&messenger, + [&redispatch_scancode](UINT cInputs, LPINPUT pInputs, + int cbSize) -> UINT { + EXPECT_TRUE(cbSize > 0); + redispatch_scancode = pInputs->ki.wScan; + return 1; + }); + + handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, L'a', false); + EXPECT_EQ(received_scancode, kHandledScanCode); + EXPECT_EQ(redispatch_scancode, 0); + received_scancode = 0; + handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', + false); + EXPECT_EQ(received_scancode, kUnhandledScanCode); + EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); +} + +TEST(KeyEventHandlerTest, ExtendedKeysAreSentToRedispatch) { + auto handled_message = CreateResponse(true); + auto unhandled_message = CreateResponse(false); + int received_scancode = 0; + bool is_extended_key = false; + + TestBinaryMessenger messenger( + [&received_scancode, &handled_message, &unhandled_message]( + const std::string& channel, const uint8_t* message, + size_t message_size, BinaryReply reply) { + if (channel == "flutter/keyevent") { + auto message_doc = JsonMessageCodec::GetInstance().DecodeMessage( + message, message_size); + received_scancode = (*message_doc)[kScanCodeKey].GetInt(); + if (received_scancode == kHandledScanCode) { + reply(handled_message->data(), handled_message->size()); + } else { + reply(unhandled_message->data(), unhandled_message->size()); + } + } + }); + + int redispatch_scancode = 0; + KeyEventHandler handler( + &messenger, + [&redispatch_scancode, &is_extended_key](UINT cInputs, LPINPUT pInputs, + int cbSize) -> UINT { + EXPECT_TRUE(cbSize > 0); + redispatch_scancode = pInputs->ki.wScan; + is_extended_key = (pInputs->ki.dwFlags & KEYEVENTF_EXTENDEDKEY) != 0; + return 1; + }); + + // Extended key flag is passed to redispatched events if set. + handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', true); + EXPECT_EQ(received_scancode, kUnhandledScanCode); + EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); + EXPECT_EQ(is_extended_key, true); + + // Extended key flag is not passed to redispatched events if not set. + handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', + false); + EXPECT_EQ(received_scancode, kUnhandledScanCode); + EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); + EXPECT_EQ(is_extended_key, false); +} + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/windows/text_input_plugin.cc b/shell/platform/windows/text_input_plugin.cc index b6d8abaa988e6..012aa9b3fa623 100644 --- a/shell/platform/windows/text_input_plugin.cc +++ b/shell/platform/windows/text_input_plugin.cc @@ -67,7 +67,7 @@ bool TextInputPlugin::KeyboardHook(FlutterWindowsView* view, int action, char32_t character, bool extended, - bool wasDown) { + bool was_down) { if (active_model_ == nullptr) { return false; } diff --git a/shell/platform/windows/text_input_plugin.h b/shell/platform/windows/text_input_plugin.h index 46197d9a122b1..7a03c2a12da29 100644 --- a/shell/platform/windows/text_input_plugin.h +++ b/shell/platform/windows/text_input_plugin.h @@ -14,7 +14,7 @@ #include "flutter/shell/platform/common/cpp/geometry.h" #include "flutter/shell/platform/common/cpp/json_method_codec.h" #include "flutter/shell/platform/common/cpp/text_input_model.h" -#include "flutter/shell/platform/windows/keyboard_hook_handler.h" +#include "flutter/shell/platform/windows/keyboard_handler_base.h" #include "flutter/shell/platform/windows/public/flutter_windows.h" #include "flutter/shell/platform/windows/text_input_plugin_delegate.h" @@ -25,32 +25,32 @@ class FlutterWindowsView; // Implements a text input plugin. // // Specifically handles window events within windows. -class TextInputPlugin : public KeyboardHookHandler { +class TextInputPlugin : public KeyboardHandlerBase { public: explicit TextInputPlugin(flutter::BinaryMessenger* messenger, TextInputPluginDelegate* delegate); virtual ~TextInputPlugin(); - // |KeyboardHookHandler| + // |KeyboardHandlerBase| bool KeyboardHook(FlutterWindowsView* view, int key, int scancode, int action, char32_t character, bool extended, - bool wasDown) override; + bool was_down) override; - // |KeyboardHookHandler| + // |KeyboardHandlerBase| void TextHook(FlutterWindowsView* view, const std::u16string& text) override; - // |KeyboardHookHandler| + // |KeyboardHandlerBase| void ComposeBeginHook() override; - // |KeyboardHookHandler| + // |KeyboardHandlerBase| void ComposeEndHook() override; - // |KeyboardHookHandler| + // |KeyboardHandlerBase| void ComposeChangeHook(const std::u16string& text, int cursor_pos) override; private: diff --git a/shell/platform/windows/win32_flutter_window.cc b/shell/platform/windows/win32_flutter_window.cc index 16ec2cb24c980..727b2dd154ae7 100644 --- a/shell/platform/windows/win32_flutter_window.cc +++ b/shell/platform/windows/win32_flutter_window.cc @@ -167,9 +167,9 @@ bool Win32FlutterWindow::OnKey(int key, int action, char32_t character, bool extended, - bool wasDown) { + bool was_down) { return binding_handler_delegate_->OnKey(key, scancode, action, character, - extended, wasDown); + extended, was_down); } void Win32FlutterWindow::OnComposeBegin() { diff --git a/shell/platform/windows/win32_flutter_window.h b/shell/platform/windows/win32_flutter_window.h index 4c217e64db8d6..a3f37a10da74f 100644 --- a/shell/platform/windows/win32_flutter_window.h +++ b/shell/platform/windows/win32_flutter_window.h @@ -60,7 +60,7 @@ class Win32FlutterWindow : public Win32Window, public WindowBindingHandler { int action, char32_t character, bool extended, - bool wasDown) override; + bool was_down) override; // |Win32Window| void OnComposeBegin() override; diff --git a/shell/platform/windows/win32_flutter_window_unittests.cc b/shell/platform/windows/win32_flutter_window_unittests.cc index c53843e9a437e..0287bc05c37d0 100644 --- a/shell/platform/windows/win32_flutter_window_unittests.cc +++ b/shell/platform/windows/win32_flutter_window_unittests.cc @@ -6,6 +6,7 @@ #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" #include "flutter/shell/platform/windows/flutter_windows_engine.h" +#include "flutter/shell/platform/windows/keyboard_key_handler.h" #include "flutter/shell/platform/windows/testing/engine_embedder_api_modifier.h" #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h" #include "flutter/shell/platform/windows/testing/win32_flutter_window_test.h" @@ -46,12 +47,12 @@ struct SimulatedEvent { // A key event handler that can be spied on while it forwards calls to the real // key event handler. -class SpyKeyEventHandler : public KeyboardHookHandler { +class SpyKeyEventHandler : public KeyboardHandlerBase { public: SpyKeyEventHandler(flutter::BinaryMessenger* messenger, - KeyEventHandler::SendInputDelegate delegate) { + KeyboardKeyHandler::SendInputDelegate delegate) { real_implementation_ = - std::make_unique(messenger, delegate); + std::make_unique(messenger, delegate); ON_CALL(*this, KeyboardHook(_, _, _, _, _, _)) .WillByDefault( Invoke(real_implementation_.get(), &KeyEventHandler::KeyboardHook)); @@ -79,7 +80,7 @@ class SpyKeyEventHandler : public KeyboardHookHandler { // A text input plugin that can be spied on while it forwards calls to the real // text input plugin. -class SpyTextInputPlugin : public KeyboardHookHandler, +class SpyTextInputPlugin : public KeyboardHandlerBase, public TextInputPluginDelegate { public: SpyTextInputPlugin(flutter::BinaryMessenger* messenger) { @@ -140,7 +141,7 @@ class MockWin32FlutterWindow : public Win32FlutterWindow { MOCK_METHOD2(OnScroll, void(double, double)); }; -// A FlutterWindowsView that overrides the RegisterKeyboardHookHandlers function +// A FlutterWindowsView that overrides the RegisterKeyboardHandlers function // to register the keyboard hook handlers that can be spied upon. class TestFlutterWindowsView : public FlutterWindowsView { public: @@ -164,7 +165,7 @@ class TestFlutterWindowsView : public FlutterWindowsView { } protected: - void RegisterKeyboardHookHandlers( + void RegisterKeyboardHandlers( flutter::BinaryMessenger* messenger) override { auto spy_key_event_handler = std::make_unique( messenger, [this](UINT cInputs, LPINPUT pInputs, int cbSize) -> UINT { @@ -174,8 +175,8 @@ class TestFlutterWindowsView : public FlutterWindowsView { std::make_unique(messenger); key_event_handler = spy_key_event_handler.get(); text_input_plugin = spy_text_input_plugin.get(); - AddKeyboardHookHandler(std::move(spy_key_event_handler)); - AddKeyboardHookHandler(std::move(spy_text_input_plugin)); + AddKeyboardHandlerBase(std::move(spy_key_event_handler)); + AddKeyboardHandlerBase(std::move(spy_text_input_plugin)); } private: diff --git a/shell/platform/windows/win32_window.cc b/shell/platform/windows/win32_window.cc index a06a383e4fb90..89d8bd9d3f5eb 100644 --- a/shell/platform/windows/win32_window.cc +++ b/shell/platform/windows/win32_window.cc @@ -341,9 +341,8 @@ Win32Window::HandleMessage(UINT const message, if (keycode_for_char_message_ != 0) { const unsigned int scancode = (lparam >> 16) & 0xff; const bool extended = ((lparam >> 24) & 0x01) == 0x01; - bool handled = OnKey(keycode_for_char_message_, scancode, WM_KEYDOWN, - code_point, extended); + code_point, extended, false); keycode_for_char_message_ = 0; if (handled) { // If the OnKey handler handles the message, then return so we don't @@ -396,8 +395,8 @@ Win32Window::HandleMessage(UINT const message, keyCode = MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX); } const int action = is_keydown_message ? WM_KEYDOWN : WM_KEYUP; - const bool wasDown = lparam & 0x40000000; - if (OnKey(keyCode, scancode, action, 0, extended, wasDown)) { + const bool was_down = lparam & 0x40000000; + if (OnKey(keyCode, scancode, action, 0, extended, was_down)) { return 0; } break; diff --git a/shell/platform/windows/win32_window.h b/shell/platform/windows/win32_window.h index 5107e3fbf09c1..483cfd41e76e1 100644 --- a/shell/platform/windows/win32_window.h +++ b/shell/platform/windows/win32_window.h @@ -102,7 +102,7 @@ class Win32Window { int action, char32_t character, bool extended, - bool wasDown) = 0; + bool was_down) = 0; // Called when IME composing begins. virtual void OnComposeBegin() = 0; diff --git a/shell/platform/windows/win32_window_unittests.cc b/shell/platform/windows/win32_window_unittests.cc index 325d0ca81315e..5788bbfefe161 100644 --- a/shell/platform/windows/win32_window_unittests.cc +++ b/shell/platform/windows/win32_window_unittests.cc @@ -57,7 +57,7 @@ TEST(MockWin32Window, HorizontalScroll) { TEST(MockWin32Window, KeyDown) { MockWin32Window window; - EXPECT_CALL(window, OnKey(_, _, _, _, _)).Times(1); + EXPECT_CALL(window, OnKey(_, _, _, _, _, _)).Times(1); LPARAM lparam = CreateKeyEventLparam(42); // send a "Shift" key down event. window.InjectWindowMessage(WM_KEYDOWN, 16, lparam); @@ -65,7 +65,7 @@ TEST(MockWin32Window, KeyDown) { TEST(MockWin32Window, KeyUp) { MockWin32Window window; - EXPECT_CALL(window, OnKey(_, _, _, _, _)).Times(1); + EXPECT_CALL(window, OnKey(_, _, _, _, _, _)).Times(1); LPARAM lparam = CreateKeyEventLparam(42); // send a "Shift" key up event. window.InjectWindowMessage(WM_KEYUP, 16, lparam); @@ -75,11 +75,11 @@ TEST(MockWin32Window, KeyDownPrintable) { MockWin32Window window; LPARAM lparam = CreateKeyEventLparam(30); // OnKey shouldn't be called until the WM_CHAR message. - EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false)).Times(0); + EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false, false)).Times(0); // send a "A" key down event. window.InjectWindowMessage(WM_KEYDOWN, 65, lparam); - EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false)).Times(1); + EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false, false)).Times(1); EXPECT_CALL(window, OnText(_)).Times(1); window.InjectWindowMessage(WM_CHAR, 65, lparam); } diff --git a/shell/platform/windows/window_binding_handler_delegate.h b/shell/platform/windows/window_binding_handler_delegate.h index c897e56d29265..9ca35e54b2706 100644 --- a/shell/platform/windows/window_binding_handler_delegate.h +++ b/shell/platform/windows/window_binding_handler_delegate.h @@ -49,7 +49,7 @@ class WindowBindingHandlerDelegate { int action, char32_t character, bool extended, - bool wasDown) = 0; + bool was_down) = 0; // Notifies the delegate that IME composing mode has begun. // From b62e35da867aa67b3ea93c82b688bc462eaa8da5 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 29 Jan 2021 13:47:31 -0800 Subject: [PATCH 41/82] keyboard_key_handler --- shell/platform/windows/BUILD.gn | 7 +- .../keyboard_key_channel_handler_unittests.cc | 8 +- .../platform/windows/keyboard_key_handler.cc | 12 +- shell/platform/windows/keyboard_key_handler.h | 1 + .../windows/keyboard_key_handler_unittests.cc | 291 ++++++++++++------ .../windows/win32_flutter_window_unittests.cc | 62 ++-- 6 files changed, 255 insertions(+), 126 deletions(-) diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 5a6ba55c43247..9429ffb23f68e 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -177,8 +177,9 @@ if (target_os == "winuwp") { "flutter_project_bundle_unittests.cc", "flutter_windows_engine_unittests.cc", "flutter_windows_texture_registrar_unittests.cc", - # "keyboard_key_embedder_handler_unittests.cc", - # "key_event_handler_unittests.cc", + #"keyboard_key_embedder_handler_unittests.cc", + #"keyboard_key_channel_handler_unittests.cc", + "keyboard_key_handler_unittests.cc", "string_conversion_unittests.cc", "system_utils_unittests.cc", "testing/engine_embedder_api_modifier.h", @@ -189,7 +190,7 @@ if (target_os == "winuwp") { "testing/win32_flutter_window_test.cc", "testing/win32_flutter_window_test.h", "win32_dpi_utils_unittests.cc", - # "win32_flutter_window_unittests.cc", + "win32_flutter_window_unittests.cc", "win32_window_proc_delegate_manager_unittests.cc", "win32_window_unittests.cc", ] diff --git a/shell/platform/windows/keyboard_key_channel_handler_unittests.cc b/shell/platform/windows/keyboard_key_channel_handler_unittests.cc index 9eb3bad94fda2..3ef6bf4c1b6a6 100644 --- a/shell/platform/windows/keyboard_key_channel_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_channel_handler_unittests.cc @@ -27,7 +27,7 @@ std::unique_ptr> CreateResponse(bool handled) { return JsonMessageCodec::GetInstance().EncodeMessage(*response_doc); } -TEST(KeyEventHandlerTest, KeyboardHookHandling) { +TEST(KeyboardKeyHandlerTest, KeyboardHookHandling) { auto handled_message = CreateResponse(true); auto unhandled_message = CreateResponse(false); int received_scancode = 0; @@ -49,7 +49,7 @@ TEST(KeyEventHandlerTest, KeyboardHookHandling) { }); int redispatch_scancode = 0; - KeyEventHandler handler(&messenger, + KeyboardKeyHandler handler(&messenger, [&redispatch_scancode](UINT cInputs, LPINPUT pInputs, int cbSize) -> UINT { EXPECT_TRUE(cbSize > 0); @@ -67,7 +67,7 @@ TEST(KeyEventHandlerTest, KeyboardHookHandling) { EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); } -TEST(KeyEventHandlerTest, ExtendedKeysAreSentToRedispatch) { +TEST(KeyboardKeyHandlerTest, ExtendedKeysAreSentToRedispatch) { auto handled_message = CreateResponse(true); auto unhandled_message = CreateResponse(false); int received_scancode = 0; @@ -90,7 +90,7 @@ TEST(KeyEventHandlerTest, ExtendedKeysAreSentToRedispatch) { }); int redispatch_scancode = 0; - KeyEventHandler handler( + KeyboardKeyHandler handler( &messenger, [&redispatch_scancode, &is_extended_key](UINT cInputs, LPINPUT pInputs, int cbSize) -> UINT { diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index 7eaa0d6fa4516..06101aa63bc45 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -141,16 +141,26 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, (extended ? KEYEVENTF_EXTENDEDKEY : 0x0) | (action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0)), .unreplied = delegates_.size(), + .any_handled = false, }; pending_events_.push_back(std::make_unique(std::move(pending))); PendingEvent& pending_event = *pending_events_.back(); + // If delegates_ is empty, we need to respond immediately. But this would + // never happen in real life since all delegates are added by hardcode, + // therefore we simply assert. + assert(delegates_.size() != 0); for (const auto& delegate : delegates_) { delegate->KeyboardHook(key, scancode, action, character, extended, was_down, [pending_event = &pending_event, this](bool handled) { pending_event->unreplied -= 1; + pending_event->any_handled = pending_event->any_handled || handled; if (pending_event->unreplied == 0) { - RedispatchEvent(pending_event); + if (!pending_event->any_handled) { + RedispatchEvent(pending_event); + } else { + RemovePendingEvent(pending_event->id); + } } }); } diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index b78e135f6fed5..769a8a2eb6374 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -75,6 +75,7 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { char32_t character; DWORD dwFlags; size_t unreplied; + bool any_handled; }; const PendingEvent* FindPendingEvent(uint64_t id); diff --git a/shell/platform/windows/keyboard_key_handler_unittests.cc b/shell/platform/windows/keyboard_key_handler_unittests.cc index 9eb3bad94fda2..9da4bb1efeebf 100644 --- a/shell/platform/windows/keyboard_key_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_handler_unittests.cc @@ -1,117 +1,228 @@ // 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/windows/key_event_handler.h" +#include "flutter/shell/platform/windows/keyboard_key_handler.h" #include #include -#include "flutter/shell/platform/common/cpp/json_message_codec.h" -#include "flutter/shell/platform/windows/flutter_windows_view.h" -#include "flutter/shell/platform/windows/testing/test_binary_messenger.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace flutter { namespace testing { -static constexpr char kScanCodeKey[] = "scanCode"; +namespace { + static constexpr int kHandledScanCode = 20; +static constexpr int kHandledScanCode2 = 22; static constexpr int kUnhandledScanCode = 21; -std::unique_ptr> CreateResponse(bool handled) { - auto response_doc = - std::make_unique(rapidjson::kObjectType); - auto& allocator = response_doc->GetAllocator(); - response_doc->AddMember("handled", handled, allocator); - return JsonMessageCodec::GetInstance().EncodeMessage(*response_doc); -} +class MockKeyHandlerDelegate + : public KeyboardKeyHandler::KeyboardKeyHandlerDelegate { + public: + class KeyboardHookCall { + public: + int delegate_id; + int key; + int scancode; + int action; + char32_t character; + bool extended; + bool was_down; + std::function callback; + }; + + MockKeyHandlerDelegate(int delegate_id, + std::list* hook_history) + : delegate_id(delegate_id), hook_history(hook_history) {} + virtual ~MockKeyHandlerDelegate() = default; + + virtual void KeyboardHook(int key, + int scancode, + int action, + char32_t character, + bool extended, + bool was_down, + std::function callback) { + hook_history->push_back(KeyboardHookCall{ + .delegate_id = delegate_id, + .key = key, + .scancode = scancode, + .character = character, + .extended = extended, + .was_down = was_down, + .callback = std::move(callback), + }); + } + + int delegate_id; + std::list* hook_history; +}; + +} // namespace -TEST(KeyEventHandlerTest, KeyboardHookHandling) { - auto handled_message = CreateResponse(true); - auto unhandled_message = CreateResponse(false); - int received_scancode = 0; - - TestBinaryMessenger messenger( - [&received_scancode, &handled_message, &unhandled_message]( - const std::string& channel, const uint8_t* message, - size_t message_size, BinaryReply reply) { - if (channel == "flutter/keyevent") { - auto message_doc = JsonMessageCodec::GetInstance().DecodeMessage( - message, message_size); - received_scancode = (*message_doc)[kScanCodeKey].GetInt(); - if (received_scancode == kHandledScanCode) { - reply(handled_message->data(), handled_message->size()); - } else { - reply(unhandled_message->data(), unhandled_message->size()); - } - } - }); +TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithSingleDelegate) { + std::list hook_history; + // Capture the scancode of the last redispatched event int redispatch_scancode = 0; - KeyEventHandler handler(&messenger, - [&redispatch_scancode](UINT cInputs, LPINPUT pInputs, - int cbSize) -> UINT { - EXPECT_TRUE(cbSize > 0); - redispatch_scancode = pInputs->ki.wScan; - return 1; - }); - - handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, L'a', false); - EXPECT_EQ(received_scancode, kHandledScanCode); + bool delegate_handled = false; + KeyboardKeyHandler handler([&redispatch_scancode](UINT cInputs, LPINPUT pInputs, + int cbSize) -> UINT { + EXPECT_TRUE(cbSize > 0); + redispatch_scancode = pInputs->ki.wScan; + return 1; + }); + // Add one delegate + handler.AddDelegate( + std::make_unique(1, &hook_history)); + + /// Test 1: One event that is handled by the framework + + // Dispatch a key event + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, + L'a', false, false); + EXPECT_EQ(delegate_handled, true); EXPECT_EQ(redispatch_scancode, 0); - received_scancode = 0; - handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', - false); - EXPECT_EQ(received_scancode, kUnhandledScanCode); - EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); + EXPECT_EQ(hook_history.size(), 1); + EXPECT_EQ(hook_history.back().delegate_id, 1); + EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); + EXPECT_EQ(hook_history.back().was_down, false); + + hook_history.back().callback(true); + EXPECT_EQ(redispatch_scancode, 0); + + hook_history.clear(); + + /// Test 2: Two events that are unhandled by the framework + + // Dispatch a key event. + // Also this is the same event as the previous one, to test that handled + // events are cleared from the pending list. + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, + L'a', false, false); + EXPECT_EQ(delegate_handled, true); + EXPECT_EQ(redispatch_scancode, 0); + EXPECT_EQ(hook_history.size(), 1); + EXPECT_EQ(hook_history.back().delegate_id, 1); + EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); + EXPECT_EQ(hook_history.back().was_down, false); + + // Dispatch another key event + delegate_handled = handler.KeyboardHook(nullptr, 65, kHandledScanCode2, WM_KEYUP, + L'b', false, true); + EXPECT_EQ(delegate_handled, true); + EXPECT_EQ(redispatch_scancode, 0); + EXPECT_EQ(hook_history.size(), 2); + EXPECT_EQ(hook_history.back().delegate_id, 1); + EXPECT_EQ(hook_history.back().scancode, kHandledScanCode2); + EXPECT_EQ(hook_history.back().was_down, true); + + // Resolve the second event first to test out-of-order response + hook_history.back().callback(false); + EXPECT_EQ(redispatch_scancode, kHandledScanCode2); + + // Resolve the first event then + hook_history.front().callback(false); + EXPECT_EQ(redispatch_scancode, kHandledScanCode); + + EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, + L'a', false, false), false); + EXPECT_EQ(handler.KeyboardHook(nullptr, 65, kHandledScanCode2, WM_KEYUP, + L'b', false, false), false); + + hook_history.clear(); + redispatch_scancode = 0; } -TEST(KeyEventHandlerTest, ExtendedKeysAreSentToRedispatch) { - auto handled_message = CreateResponse(true); - auto unhandled_message = CreateResponse(false); - int received_scancode = 0; - bool is_extended_key = false; - - TestBinaryMessenger messenger( - [&received_scancode, &handled_message, &unhandled_message]( - const std::string& channel, const uint8_t* message, - size_t message_size, BinaryReply reply) { - if (channel == "flutter/keyevent") { - auto message_doc = JsonMessageCodec::GetInstance().DecodeMessage( - message, message_size); - received_scancode = (*message_doc)[kScanCodeKey].GetInt(); - if (received_scancode == kHandledScanCode) { - reply(handled_message->data(), handled_message->size()); - } else { - reply(unhandled_message->data(), unhandled_message->size()); - } - } - }); +TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoDelegates) { + std::list hook_history; + // Capture the scancode of the last redispatched event int redispatch_scancode = 0; - KeyEventHandler handler( - &messenger, - [&redispatch_scancode, &is_extended_key](UINT cInputs, LPINPUT pInputs, - int cbSize) -> UINT { - EXPECT_TRUE(cbSize > 0); - redispatch_scancode = pInputs->ki.wScan; - is_extended_key = (pInputs->ki.dwFlags & KEYEVENTF_EXTENDEDKEY) != 0; - return 1; - }); - - // Extended key flag is passed to redispatched events if set. - handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', true); - EXPECT_EQ(received_scancode, kUnhandledScanCode); - EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); - EXPECT_EQ(is_extended_key, true); - - // Extended key flag is not passed to redispatched events if not set. - handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', - false); - EXPECT_EQ(received_scancode, kUnhandledScanCode); - EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); - EXPECT_EQ(is_extended_key, false); + bool delegate_handled = false; + KeyboardKeyHandler handler([&redispatch_scancode](UINT cInputs, LPINPUT pInputs, + int cbSize) -> UINT { + EXPECT_TRUE(cbSize > 0); + redispatch_scancode = pInputs->ki.wScan; + return 1; + }); + // Only add one delegate for now. + handler.AddDelegate( + std::make_unique(1, &hook_history)); + + /// Test 1: Add a delegate before an event is responded + + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, + L'a', false, false); + EXPECT_EQ(delegate_handled, true); + EXPECT_EQ(redispatch_scancode, 0); + EXPECT_EQ(hook_history.size(), 1); + EXPECT_EQ(hook_history.back().delegate_id, 1); + EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); + EXPECT_EQ(hook_history.back().was_down, false); + + handler.AddDelegate( + std::make_unique(2, &hook_history)); + + // Only one reply is needed because the 2nd delegate is added late. + hook_history.back().callback(false); + EXPECT_EQ(redispatch_scancode, kHandledScanCode); + + EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, + L'a', false, false), false); + + redispatch_scancode = 0; + hook_history.clear(); + + /// Test 2: A delegate responds true + + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, + L'a', false, false); + EXPECT_EQ(delegate_handled, true); + EXPECT_EQ(redispatch_scancode, 0); + EXPECT_EQ(hook_history.size(), 2); + EXPECT_EQ(hook_history.front().delegate_id, 1); + EXPECT_EQ(hook_history.front().scancode, kHandledScanCode); + EXPECT_EQ(hook_history.front().was_down, false); + EXPECT_EQ(hook_history.back().delegate_id, 2); + EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); + EXPECT_EQ(hook_history.back().was_down, false); + + hook_history.back().callback(true); + EXPECT_EQ(redispatch_scancode, 0); + + hook_history.front().callback(false); + EXPECT_EQ(redispatch_scancode, 0); + + redispatch_scancode = 0; + hook_history.clear(); + + /// Test 3: All delegates respond false + + // Also this is the same event as the previous one, to test that handled + // events are cleared from the pending list. + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, + L'a', false, false); + EXPECT_EQ(delegate_handled, true); + EXPECT_EQ(redispatch_scancode, 0); + EXPECT_EQ(hook_history.size(), 2); + EXPECT_EQ(hook_history.front().delegate_id, 1); + EXPECT_EQ(hook_history.front().scancode, kHandledScanCode); + EXPECT_EQ(hook_history.front().was_down, false); + EXPECT_EQ(hook_history.back().delegate_id, 2); + EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); + EXPECT_EQ(hook_history.back().was_down, false); + + hook_history.front().callback(false); + EXPECT_EQ(redispatch_scancode, 0); + + hook_history.back().callback(false); + EXPECT_EQ(redispatch_scancode, kHandledScanCode); + + EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, + L'a', false, false), false); } } // namespace testing diff --git a/shell/platform/windows/win32_flutter_window_unittests.cc b/shell/platform/windows/win32_flutter_window_unittests.cc index 0287bc05c37d0..c2208ca7307cf 100644 --- a/shell/platform/windows/win32_flutter_window_unittests.cc +++ b/shell/platform/windows/win32_flutter_window_unittests.cc @@ -7,10 +7,12 @@ #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" #include "flutter/shell/platform/windows/flutter_windows_engine.h" #include "flutter/shell/platform/windows/keyboard_key_handler.h" +#include "flutter/shell/platform/windows/keyboard_key_channel_handler.h" #include "flutter/shell/platform/windows/testing/engine_embedder_api_modifier.h" #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h" #include "flutter/shell/platform/windows/testing/win32_flutter_window_test.h" #include "flutter/shell/platform/windows/text_input_plugin_delegate.h" +#include "flutter/shell/platform/windows/text_input_plugin.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -30,9 +32,9 @@ static LPARAM CreateKeyEventLparam(USHORT ScanCode, bool extended = false, USHORT RepeatCount = 1, bool ContextCode = 0, - bool PreviousKeyState = 1, + bool WasDown = 1, bool TransitionState = 1) { - return ((LPARAM(TransitionState) << 31) | (LPARAM(PreviousKeyState) << 30) | + return ((LPARAM(TransitionState) << 31) | (LPARAM(WasDown) << 30) | (LPARAM(ContextCode) << 29) | (LPARAM(extended ? 0x1 : 0x0) << 24) | (LPARAM(ScanCode) << 16) | LPARAM(RepeatCount)); } @@ -47,27 +49,30 @@ struct SimulatedEvent { // A key event handler that can be spied on while it forwards calls to the real // key event handler. -class SpyKeyEventHandler : public KeyboardHandlerBase { +class SpyKeyboardKeyHandler : public KeyboardHandlerBase { public: - SpyKeyEventHandler(flutter::BinaryMessenger* messenger, + SpyKeyboardKeyHandler(flutter::BinaryMessenger* messenger, KeyboardKeyHandler::SendInputDelegate delegate) { real_implementation_ = - std::make_unique(messenger, delegate); - ON_CALL(*this, KeyboardHook(_, _, _, _, _, _)) + std::make_unique(delegate); + real_implementation_->AddDelegate( + std::make_unique(messenger)); + ON_CALL(*this, KeyboardHook(_, _, _, _, _, _, _)) .WillByDefault( - Invoke(real_implementation_.get(), &KeyEventHandler::KeyboardHook)); + Invoke(real_implementation_.get(), &KeyboardKeyHandler::KeyboardHook)); ON_CALL(*this, TextHook(_, _)) .WillByDefault( - Invoke(real_implementation_.get(), &KeyEventHandler::TextHook)); + Invoke(real_implementation_.get(), &KeyboardKeyHandler::TextHook)); } - MOCK_METHOD6(KeyboardHook, + MOCK_METHOD7(KeyboardHook, bool(FlutterWindowsView* window, int key, int scancode, int action, char32_t character, - bool extended)); + bool extended, + bool was_down)); MOCK_METHOD2(TextHook, void(FlutterWindowsView* window, const std::u16string& text)); MOCK_METHOD0(ComposeBeginHook, void()); @@ -75,7 +80,7 @@ class SpyKeyEventHandler : public KeyboardHandlerBase { MOCK_METHOD2(ComposeChangeHook, void(const std::u16string& text, int cursor_pos)); private: - std::unique_ptr real_implementation_; + std::unique_ptr real_implementation_; }; // A text input plugin that can be spied on while it forwards calls to the real @@ -85,7 +90,7 @@ class SpyTextInputPlugin : public KeyboardHandlerBase, public: SpyTextInputPlugin(flutter::BinaryMessenger* messenger) { real_implementation_ = std::make_unique(messenger, this); - ON_CALL(*this, KeyboardHook(_, _, _, _, _, _)) + ON_CALL(*this, KeyboardHook(_, _, _, _, _, _, _)) .WillByDefault( Invoke(real_implementation_.get(), &TextInputPlugin::KeyboardHook)); ON_CALL(*this, TextHook(_, _)) @@ -93,13 +98,14 @@ class SpyTextInputPlugin : public KeyboardHandlerBase, Invoke(real_implementation_.get(), &TextInputPlugin::TextHook)); } - MOCK_METHOD6(KeyboardHook, + MOCK_METHOD7(KeyboardHook, bool(FlutterWindowsView* window, int key, int scancode, int action, char32_t character, - bool extended)); + bool extended, + bool was_down)); MOCK_METHOD2(TextHook, void(FlutterWindowsView* window, const std::u16string& text)); MOCK_METHOD0(ComposeBeginHook, void()); @@ -152,7 +158,7 @@ class TestFlutterWindowsView : public FlutterWindowsView { virtual_key_(virtual_key), is_printable_(is_printable) {} - SpyKeyEventHandler* key_event_handler; + SpyKeyboardKeyHandler* key_event_handler; SpyTextInputPlugin* text_input_plugin; void InjectPendingEvents(MockWin32FlutterWindow* win32window) { @@ -167,7 +173,7 @@ class TestFlutterWindowsView : public FlutterWindowsView { protected: void RegisterKeyboardHandlers( flutter::BinaryMessenger* messenger) override { - auto spy_key_event_handler = std::make_unique( + auto spy_key_event_handler = std::make_unique( messenger, [this](UINT cInputs, LPINPUT pInputs, int cbSize) -> UINT { return this->SendInput(cInputs, pInputs, cbSize); }); @@ -301,11 +307,11 @@ TEST(Win32FlutterWindowTest, NonPrintableKeyDownPropagation) { flutter_windows_view.SetEngine(std::move(GetTestEngine())); EXPECT_CALL(*flutter_windows_view.key_event_handler, KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, - false /* extended */)) + false /* extended */, true)) .Times(2) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.text_input_plugin, - KeyboardHook(_, _, _, _, _, _)) + KeyboardHook(_, _, _, _, _, _, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.key_event_handler, TextHook(_, _)) @@ -322,11 +328,11 @@ TEST(Win32FlutterWindowTest, NonPrintableKeyDownPropagation) { test_response = true; EXPECT_CALL(*flutter_windows_view.key_event_handler, KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, - false /* extended */)) + false /* extended */, true)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.text_input_plugin, - KeyboardHook(_, _, _, _, _, _)) + KeyboardHook(_, _, _, _, _, _, _)) .Times(0); EXPECT_EQ(win32window.InjectWindowMessage(WM_KEYDOWN, virtual_key, lparam), 0); @@ -356,11 +362,11 @@ TEST(Win32FlutterWindowTest, CharKeyDownPropagation) { flutter_windows_view.SetEngine(std::move(GetTestEngine())); EXPECT_CALL( *flutter_windows_view.key_event_handler, - KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, false)) + KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, false, false)) .Times(2) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.text_input_plugin, - KeyboardHook(_, _, _, _, _, _)) + KeyboardHook(_, _, _, _, _, _, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.key_event_handler, TextHook(_, _)) @@ -380,11 +386,11 @@ TEST(Win32FlutterWindowTest, CharKeyDownPropagation) { test_response = true; EXPECT_CALL(*flutter_windows_view.key_event_handler, KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, - false /* is_printable */)) + false /* is_printable */, false)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.text_input_plugin, - KeyboardHook(_, _, _, _, _, _)) + KeyboardHook(_, _, _, _, _, _, _)) .Times(0); EXPECT_CALL(*flutter_windows_view.key_event_handler, TextHook(_, _)) .Times(0); @@ -419,11 +425,11 @@ TEST(Win32FlutterWindowTest, ModifierKeyDownPropagation) { flutter_windows_view.SetEngine(std::move(GetTestEngine())); EXPECT_CALL(*flutter_windows_view.key_event_handler, KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, - false /* extended */)) + false /* extended */, true)) .Times(2) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.text_input_plugin, - KeyboardHook(_, _, _, _, _, _)) + KeyboardHook(_, _, _, _, _, _, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.key_event_handler, TextHook(_, _)) @@ -440,11 +446,11 @@ TEST(Win32FlutterWindowTest, ModifierKeyDownPropagation) { test_response = true; EXPECT_CALL(*flutter_windows_view.key_event_handler, KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, - false /* extended */)) + false /* extended */, true)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.text_input_plugin, - KeyboardHook(_, _, _, _, _, _)) + KeyboardHook(_, _, _, _, _, _, _)) .Times(0); EXPECT_EQ(win32window.InjectWindowMessage(WM_KEYDOWN, virtual_key, lparam), 0); From 951aaa854328308fcd113c679714f72c1cce2ece Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 29 Jan 2021 14:11:43 -0800 Subject: [PATCH 42/82] channel handler --- shell/platform/windows/BUILD.gn | 2 +- .../keyboard_key_channel_handler_unittests.cc | 60 ++++++++----------- 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 9429ffb23f68e..e362cb2e122ab 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -178,7 +178,7 @@ if (target_os == "winuwp") { "flutter_windows_engine_unittests.cc", "flutter_windows_texture_registrar_unittests.cc", #"keyboard_key_embedder_handler_unittests.cc", - #"keyboard_key_channel_handler_unittests.cc", + "keyboard_key_channel_handler_unittests.cc", "keyboard_key_handler_unittests.cc", "string_conversion_unittests.cc", "system_utils_unittests.cc", diff --git a/shell/platform/windows/keyboard_key_channel_handler_unittests.cc b/shell/platform/windows/keyboard_key_channel_handler_unittests.cc index 3ef6bf4c1b6a6..7d2993f95a826 100644 --- a/shell/platform/windows/keyboard_key_channel_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_channel_handler_unittests.cc @@ -1,7 +1,7 @@ // 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/windows/key_event_handler.h" +#include "flutter/shell/platform/windows/keyboard_key_channel_handler.h" #include #include @@ -27,7 +27,7 @@ std::unique_ptr> CreateResponse(bool handled) { return JsonMessageCodec::GetInstance().EncodeMessage(*response_doc); } -TEST(KeyboardKeyHandlerTest, KeyboardHookHandling) { +TEST(KeyboardKeyChannelHandlerTest, KeyboardHookHandling) { auto handled_message = CreateResponse(true); auto unhandled_message = CreateResponse(false); int received_scancode = 0; @@ -48,30 +48,28 @@ TEST(KeyboardKeyHandlerTest, KeyboardHookHandling) { } }); - int redispatch_scancode = 0; - KeyboardKeyHandler handler(&messenger, - [&redispatch_scancode](UINT cInputs, LPINPUT pInputs, - int cbSize) -> UINT { - EXPECT_TRUE(cbSize > 0); - redispatch_scancode = pInputs->ki.wScan; - return 1; - }); + KeyboardKeyChannelHandler handler(&messenger); + bool last_handled = false; - handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, L'a', false); + handler.KeyboardHook(64, kHandledScanCode, WM_KEYDOWN, L'a', + false, false, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(received_scancode, kHandledScanCode); - EXPECT_EQ(redispatch_scancode, 0); + EXPECT_EQ(last_handled, true); + received_scancode = 0; - handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', - false); + + handler.KeyboardHook(64, kUnhandledScanCode, WM_KEYDOWN, L'b', + false, false, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(received_scancode, kUnhandledScanCode); - EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); + EXPECT_EQ(last_handled, false); } -TEST(KeyboardKeyHandlerTest, ExtendedKeysAreSentToRedispatch) { +TEST(KeyboardKeyChannelHandlerTest, ExtendedKeysAreSentToRedispatch) { auto handled_message = CreateResponse(true); auto unhandled_message = CreateResponse(false); int received_scancode = 0; - bool is_extended_key = false; TestBinaryMessenger messenger( [&received_scancode, &handled_message, &unhandled_message]( @@ -89,29 +87,23 @@ TEST(KeyboardKeyHandlerTest, ExtendedKeysAreSentToRedispatch) { } }); - int redispatch_scancode = 0; - KeyboardKeyHandler handler( - &messenger, - [&redispatch_scancode, &is_extended_key](UINT cInputs, LPINPUT pInputs, - int cbSize) -> UINT { - EXPECT_TRUE(cbSize > 0); - redispatch_scancode = pInputs->ki.wScan; - is_extended_key = (pInputs->ki.dwFlags & KEYEVENTF_EXTENDEDKEY) != 0; - return 1; - }); + KeyboardKeyChannelHandler handler(&messenger); + bool last_handled = true; // Extended key flag is passed to redispatched events if set. - handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', true); + handler.KeyboardHook(64, kUnhandledScanCode, WM_KEYDOWN, L'b', + true, false, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); EXPECT_EQ(received_scancode, kUnhandledScanCode); - EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); - EXPECT_EQ(is_extended_key, true); + last_handled = true; // Extended key flag is not passed to redispatched events if not set. - handler.KeyboardHook(nullptr, 64, kUnhandledScanCode, WM_KEYDOWN, L'b', - false); + handler.KeyboardHook(64, kUnhandledScanCode, WM_KEYDOWN, L'b', + false, false, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); EXPECT_EQ(received_scancode, kUnhandledScanCode); - EXPECT_EQ(redispatch_scancode, kUnhandledScanCode); - EXPECT_EQ(is_extended_key, false); } } // namespace testing From 40f0307989e8a9474c4dc54a273e308bc91eb1ae Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 29 Jan 2021 17:04:49 -0800 Subject: [PATCH 43/82] embedder handler --- shell/platform/windows/BUILD.gn | 2 +- shell/platform/windows/flutter_key_map.cc | 6 +- .../platform/windows/flutter_windows_view.cc | 2 +- .../windows/keyboard_key_embedder_handler.cc | 16 +-- .../windows/keyboard_key_embedder_handler.h | 8 +- ...keyboard_key_embedder_handler_unittests.cc | 120 +++++++++++------- 6 files changed, 89 insertions(+), 65 deletions(-) diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index e362cb2e122ab..914bdd348bacf 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -177,7 +177,7 @@ if (target_os == "winuwp") { "flutter_project_bundle_unittests.cc", "flutter_windows_engine_unittests.cc", "flutter_windows_texture_registrar_unittests.cc", - #"keyboard_key_embedder_handler_unittests.cc", + "keyboard_key_embedder_handler_unittests.cc", "keyboard_key_channel_handler_unittests.cc", "keyboard_key_handler_unittests.cc", "string_conversion_unittests.cc", diff --git a/shell/platform/windows/flutter_key_map.cc b/shell/platform/windows/flutter_key_map.cc index f5dac22e16dc7..c5db8db546a49 100644 --- a/shell/platform/windows/flutter_key_map.cc +++ b/shell/platform/windows/flutter_key_map.cc @@ -19,7 +19,7 @@ namespace flutter { -std::map FlutterKeyboardKeyEmbedderHandler::windowsToPhysicalMap_ = { +std::map KeyboardKeyEmbedderHandler::windowsToPhysicalMap_ = { {0x0000e05f, 0x00010082}, // sleep {0x0000e063, 0x00010083}, // wakeUp {0x000000ff, 0x00070001}, // usbErrorRollOver @@ -179,7 +179,7 @@ std::map FlutterKeyboardKeyEmbedderHandler::windowsToPhysica {0x0000e066, 0x000c022a}, // browserFavorites }; -std::map FlutterKeyboardKeyEmbedderHandler::windowsToLogicalMap_ = { +std::map KeyboardKeyEmbedderHandler::windowsToLogicalMap_ = { {0x00000008, 0x00000000008}, // BACK {0x00000009, 0x00000000009}, // TAB {0x0000000d, 0x0000000000d}, // RETURN @@ -300,7 +300,7 @@ std::map FlutterKeyboardKeyEmbedderHandler::windowsToLogical {0x000000a1, 0x0040000010d}, // RSHIFT }; -std::map FlutterKeyboardKeyEmbedderHandler::scanCodeToLogicalMap_ = { +std::map KeyboardKeyEmbedderHandler::scanCodeToLogicalMap_ = { {0x0000e01d, 0x0400000105}, // ControlRight {0x0000e038, 0x0400000102}, // AltRight {0x0000004f, 0x0200000031}, // Numpad1 diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 38c12f9e8498b..9504fb88220d9 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -55,7 +55,7 @@ void FlutterWindowsView::RegisterKeyboardHandlers( auto key_handler = std::make_unique(); key_handler->AddDelegate(std::make_unique( messenger)); - key_handler->AddDelegate(std::make_unique( + key_handler->AddDelegate(std::make_unique( [this](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* user_data) { diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index 9557847cfecf4..434fb0fc1f754 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -45,13 +45,13 @@ constexpr uint64_t kWindowsKeyIdPlane = 0x00700000000; */ constexpr uint64_t kAutogeneratedMask = 0x10000000000; -FlutterKeyboardKeyEmbedderHandler::FlutterKeyboardKeyEmbedderHandler( +KeyboardKeyEmbedderHandler::KeyboardKeyEmbedderHandler( std::function send_event) : sendEvent_(send_event), response_id_(1) {} -FlutterKeyboardKeyEmbedderHandler::~FlutterKeyboardKeyEmbedderHandler() = default; +KeyboardKeyEmbedderHandler::~KeyboardKeyEmbedderHandler() = default; static bool isAsciiPrintable(int codeUnit) { return codeUnit <= 0x7f && codeUnit >= 0x20; @@ -69,7 +69,7 @@ static uint16_t normalizeScancode(int windowsScanCode, bool extended) { return (windowsScanCode & 0xff) | (extended ? 0xe000 : 0); } -uint64_t FlutterKeyboardKeyEmbedderHandler::getPhysicalKey(int scancode, bool extended) { +uint64_t KeyboardKeyEmbedderHandler::getPhysicalKey(int scancode, bool extended) { int chromiumScancode = normalizeScancode(scancode, extended); auto resultIt = windowsToPhysicalMap_.find(chromiumScancode); if (resultIt != windowsToPhysicalMap_.end()) @@ -77,7 +77,7 @@ uint64_t FlutterKeyboardKeyEmbedderHandler::getPhysicalKey(int scancode, bool ex return scancode | kHidPlane; } -uint64_t FlutterKeyboardKeyEmbedderHandler::getLogicalKey(int key, bool extended, int scancode) { +uint64_t KeyboardKeyEmbedderHandler::getLogicalKey(int key, bool extended, int scancode) { // Normally logical keys should only be derived from key codes, but since some // key codes are either 0 or ambiguous (multiple keys using the same key // code), these keys are resolved by scan codes. @@ -111,7 +111,7 @@ uint64_t FlutterKeyboardKeyEmbedderHandler::getLogicalKey(int key, bool extended } } -void FlutterKeyboardKeyEmbedderHandler::cacheUtf8String(char32_t character) { +void KeyboardKeyEmbedderHandler::cacheUtf8String(char32_t character) { if (character == 0) { character_cache_[0] = '\0'; return; @@ -121,7 +121,7 @@ void FlutterKeyboardKeyEmbedderHandler::cacheUtf8String(char32_t character) { strcpy_s(character_cache_, kCharacterCacheSize, Utf8FromUtf16(text).c_str()); } -void FlutterKeyboardKeyEmbedderHandler::KeyboardHook( +void KeyboardKeyEmbedderHandler::KeyboardHook( int key, int scancode, int action, @@ -229,11 +229,11 @@ void FlutterKeyboardKeyEmbedderHandler::KeyboardHook( auto pending_ptr = std::make_unique(std::move(pending)); pending_responses_[response_id] = std::move(pending_ptr); sendEvent_(key_data, - FlutterKeyboardKeyEmbedderHandler::HandleResponse, + KeyboardKeyEmbedderHandler::HandleResponse, reinterpret_cast(pending_responses_[response_id].get())); } -void FlutterKeyboardKeyEmbedderHandler::HandleResponse( +void KeyboardKeyEmbedderHandler::HandleResponse( bool handled, void* user_data) { PendingResponse* pending = reinterpret_cast(user_data); auto callback = std::move(pending->callback); diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index 3c6bd6d2777c3..b045a8f31a626 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -28,20 +28,20 @@ constexpr size_t kCharacterCacheSize = 8; // // This class converts received keyboard events to Flutter events, // sends them to the engine, waits for response. -class FlutterKeyboardKeyEmbedderHandler +class KeyboardKeyEmbedderHandler : public KeyboardKeyHandler::KeyboardKeyHandlerDelegate { public: - // Build a FlutterKeyboardKeyEmbedderHandler. + // Build a KeyboardKeyEmbedderHandler. // // Use `send_event` to define how the manager should dispatch converted // flutter events, as well as how to receive the resopnse, to the engine. It's // typically FlutterWindowsEngine::SendKeyEvent. - explicit FlutterKeyboardKeyEmbedderHandler( + explicit KeyboardKeyEmbedderHandler( std::function send_event); - virtual ~FlutterKeyboardKeyEmbedderHandler(); + virtual ~KeyboardKeyEmbedderHandler(); // |KeyboardHandlerBase| void KeyboardHook(int key, diff --git a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index 9e53b3659f30e..bfdf48ab21814 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -4,6 +4,8 @@ #include "flutter/shell/platform/windows/keyboard_key_embedder_handler.h" +#include + #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" #include "flutter/shell/platform/windows/testing/engine_embedder_api_modifier.h" @@ -13,30 +15,28 @@ namespace flutter { namespace { -std::unique_ptr allocCopyString(const char* src) { - if (src == nullptr) - return nullptr; - size_t len = strlen(src); - std::unique_ptr dst = std::make_unique(len + 1); - memcpy(dst.get(), src, (len + 1) * sizeof(char)); - return dst; -} - class TestFlutterKeyEvent : public FlutterKeyEvent { public: - TestFlutterKeyEvent(const FlutterKeyEvent& src) { + TestFlutterKeyEvent(const FlutterKeyEvent& src, + FlutterKeyEventCallback callback, + void* user_data) + : character_str(src.character), + callback(callback), + user_data(user_data) { struct_size = src.struct_size; timestamp = src.timestamp; - kind = src.kind; + type = src.type; physical = src.physical; logical = src.logical; - character_ptr = allocCopyString(src.character); - character = character_ptr.get(); + character = character_str.c_str(); synthesized = src.synthesized; } + FlutterKeyEventCallback callback; + void* user_data; + private: - std::unique_ptr character_ptr; + const std::string character_str; }; } // namespace @@ -46,7 +46,7 @@ namespace testing { namespace { constexpr uint64_t kPhysicalKeyA = 0x1e; constexpr uint64_t kPhysicalNumpad1 = 0x4f; -constexpr uint64_t kPhysicalNumLock = 0x145; +constexpr uint64_t kPhysicalNumLock = 0x45; constexpr uint64_t kLogicalKeyA = 0x41; constexpr uint64_t kLogicalNumpad1 = 0x61; @@ -55,70 +55,91 @@ constexpr uint64_t kLogicalNumLock = 0x90; } // namespace // Test the most basic key events. -TEST(FlutterKeyboardKeyEmbedderHandler, BasicKeyPressingAndHolding) { +TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { std::vector results; TestFlutterKeyEvent* event; - - std::unique_ptr manager = - std::make_unique( - [&results](const FlutterKeyEvent& event) { - results.emplace_back(event); + bool last_handled = false; + + std::unique_ptr handler = + std::make_unique( + [&results](const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, + void* user_data) { + results.emplace_back(event, callback, user_data); }); // On a US keyboard: // Press KeyA. - manager->KeyboardHook(nullptr, kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', - false); + handler->KeyboardHook(kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', + false, false, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); EXPECT_EQ(results.size(), 1); event = &results[0]; - EXPECT_EQ(event->kind, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); EXPECT_EQ(event->physical, 0x00070004); EXPECT_EQ(event->logical, 0x00000061); EXPECT_STREQ(event->character, "a"); EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); // Hold KeyA. - manager->KeyboardHook(nullptr, kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', - true); + handler->KeyboardHook(kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', + false, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, true); EXPECT_EQ(results.size(), 1); event = &results[0]; - EXPECT_EQ(event->kind, kFlutterKeyEventTypeRepeat); + EXPECT_EQ(event->type, kFlutterKeyEventTypeRepeat); EXPECT_EQ(event->physical, 0x00070004); EXPECT_EQ(event->logical, 0x00000061); EXPECT_STREQ(event->character, "a"); EXPECT_EQ(event->synthesized, false); + + event->callback(false, event->user_data); + EXPECT_EQ(last_handled, false); + results.clear(); // Release KeyA. - manager->KeyboardHook(nullptr, kLogicalKeyA, kPhysicalKeyA, WM_KEYUP, 0, - true); + handler->KeyboardHook(kLogicalKeyA, kPhysicalKeyA, WM_KEYUP, 0, + false, true, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; - EXPECT_EQ(event->kind, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); EXPECT_EQ(event->physical, 0x00070004); EXPECT_EQ(event->logical, 0x00000061); EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, false); + event->callback(false, event->user_data); } -TEST(FlutterKeyboardKeyEmbedderHandler, ToggleNumLockDuringNumpadPress) { +TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { std::vector results; TestFlutterKeyEvent* event; - - std::unique_ptr manager = - std::make_unique( - [&results](const FlutterKeyEvent& event) { - results.emplace_back(event); + bool last_handled = false; + + std::unique_ptr handler = + std::make_unique( + [&results](const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, + void* user_data) { + results.emplace_back(event, callback, user_data); }); // On a US keyboard: // Press NumPad1. - manager->KeyboardHook(nullptr, kLogicalNumpad1, kPhysicalNumpad1, WM_KEYDOWN, - 0, false); + handler->KeyboardHook(kLogicalNumpad1, kPhysicalNumpad1, WM_KEYDOWN, 0, + false, false, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; - EXPECT_EQ(event->kind, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); EXPECT_EQ(event->physical, 0x00070059); EXPECT_EQ(event->logical, 0x00200000031); // EXPECT_STREQ(event->character, "1"); // TODO @@ -126,11 +147,12 @@ TEST(FlutterKeyboardKeyEmbedderHandler, ToggleNumLockDuringNumpadPress) { results.clear(); // Press NumLock. - manager->KeyboardHook(nullptr, kLogicalNumLock, kPhysicalNumLock, WM_KEYDOWN, - 0, false); + handler->KeyboardHook(kLogicalNumLock, kPhysicalNumLock, WM_KEYDOWN, 0, + true, false, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; - EXPECT_EQ(event->kind, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); EXPECT_EQ(event->physical, 0x00070053); EXPECT_EQ(event->logical, 0x0000010a); EXPECT_STREQ(event->character, ""); @@ -138,11 +160,12 @@ TEST(FlutterKeyboardKeyEmbedderHandler, ToggleNumLockDuringNumpadPress) { results.clear(); // Release NumLock. - manager->KeyboardHook(nullptr, kLogicalNumLock, kPhysicalNumLock, WM_KEYUP, 0, - false); + handler->KeyboardHook(kLogicalNumLock, kPhysicalNumLock, WM_KEYUP, 0, + true, true, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; - EXPECT_EQ(event->kind, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); EXPECT_EQ(event->physical, 0x00070053); EXPECT_EQ(event->logical, 0x0000010a); EXPECT_STREQ(event->character, ""); @@ -150,11 +173,12 @@ TEST(FlutterKeyboardKeyEmbedderHandler, ToggleNumLockDuringNumpadPress) { results.clear(); // Release NumPad1. (The logical key is now NumpadEnd) - manager->KeyboardHook(nullptr, kLogicalNumpadEnd, kPhysicalNumpad1, WM_KEYUP, - 0, false); + handler->KeyboardHook(kLogicalNumpadEnd, kPhysicalNumpad1, WM_KEYUP, 0, + false, true, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; - EXPECT_EQ(event->kind, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); EXPECT_EQ(event->physical, 0x00070059); EXPECT_EQ(event->logical, 0x00200000031); EXPECT_STREQ(event->character, ""); From 957cfad3e526a25f5c9fbc8aadc8788110d4a534 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 8 Feb 2021 15:21:03 -0800 Subject: [PATCH 44/82] Fix compile --- shell/platform/windows/BUILD.gn | 3 --- shell/platform/windows/client_wrapper/BUILD.gn | 2 +- shell/platform/windows/flutter_windows.cc | 2 +- shell/platform/windows/keyboard_key_channel_handler.cc | 2 +- shell/platform/windows/keyboard_key_channel_handler.h | 4 ++-- shell/platform/windows/keyboard_key_handler.cc | 2 +- 6 files changed, 6 insertions(+), 9 deletions(-) diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index ab08075841f44..c5122027bcd1c 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -182,9 +182,6 @@ executable("flutter_windows_unittests") { sources = [ # "flutter_project_bundle_unittests.cc", //TODO failing due to switches test failing. Blocked on https://github.com/flutter/flutter/issues/74153 # "flutter_windows_engine_unittests.cc", //TODO failing to send / receive platform message get plugins working first. Blocked on https://github.com/flutter/flutter/issues/74155 - "string_conversion_unittests.cc", - "system_utils_unittests.cc", - "testing/engine_embedder_api_modifier.h", ] # Target-specific sources. diff --git a/shell/platform/windows/client_wrapper/BUILD.gn b/shell/platform/windows/client_wrapper/BUILD.gn index 232f8951c1263..18cf24d75abd3 100644 --- a/shell/platform/windows/client_wrapper/BUILD.gn +++ b/shell/platform/windows/client_wrapper/BUILD.gn @@ -18,7 +18,7 @@ _wrapper_sources = [ "flutter_view_controller.cc", ] -# This code will be merged into .../common/cpp/client_wrapper for client use, +# This code will be merged into .../common/client_wrapper for client use, # so uses header paths that assume the merged state. Include the header # directories of the core wrapper files so these includes will work. config("relative_core_wrapper_headers") { diff --git a/shell/platform/windows/flutter_windows.cc b/shell/platform/windows/flutter_windows.cc index 4e1ef6ecc5af1..625b5e3465385 100644 --- a/shell/platform/windows/flutter_windows.cc +++ b/shell/platform/windows/flutter_windows.cc @@ -148,7 +148,7 @@ void FlutterDesktopResyncOutputStreams() { std::ios::sync_with_stdio(); } -// Implementations of common/cpp/ API methods. +// Implementations of common/ API methods. FlutterDesktopMessengerRef FlutterDesktopPluginRegistrarGetMessenger( FlutterDesktopPluginRegistrarRef registrar) { diff --git a/shell/platform/windows/keyboard_key_channel_handler.cc b/shell/platform/windows/keyboard_key_channel_handler.cc index d70fe68bdbc98..4c127678926f1 100644 --- a/shell/platform/windows/keyboard_key_channel_handler.cc +++ b/shell/platform/windows/keyboard_key_channel_handler.cc @@ -8,7 +8,7 @@ #include -#include "flutter/shell/platform/common/cpp/json_message_codec.h" +#include "flutter/shell/platform/common/json_message_codec.h" namespace flutter { diff --git a/shell/platform/windows/keyboard_key_channel_handler.h b/shell/platform/windows/keyboard_key_channel_handler.h index 255b6ab22e8b8..9da385235e1cc 100644 --- a/shell/platform/windows/keyboard_key_channel_handler.h +++ b/shell/platform/windows/keyboard_key_channel_handler.h @@ -9,8 +9,8 @@ #include #include -#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h" -#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h" +#include "flutter/shell/platform/common/client_wrapper/include/flutter/basic_message_channel.h" +#include "flutter/shell/platform/common/client_wrapper/include/flutter/binary_messenger.h" #include "flutter/shell/platform/windows/keyboard_key_handler.h" #include "flutter/shell/platform/windows/public/flutter_windows.h" #include "rapidjson/document.h" diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index 06101aa63bc45..266a51858f835 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -8,7 +8,7 @@ #include -#include "flutter/shell/platform/common/cpp/json_message_codec.h" +#include "flutter/shell/platform/common/json_message_codec.h" namespace flutter { From 2318bd014377cbc241407a56cc56dba33f4da757 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 8 Feb 2021 15:24:27 -0800 Subject: [PATCH 45/82] Fix delegate returning false --- .../windows/keyboard_key_channel_handler.cc | 5 ++-- .../windows/keyboard_key_channel_handler.h | 2 +- .../windows/keyboard_key_embedder_handler.cc | 9 ++++--- .../windows/keyboard_key_embedder_handler.h | 2 +- .../platform/windows/keyboard_key_handler.cc | 27 ++++++++++++------- shell/platform/windows/keyboard_key_handler.h | 5 +++- .../windows/keyboard_key_handler_unittests.cc | 3 ++- shell/platform/windows/win32_window.cc | 6 ----- 8 files changed, 33 insertions(+), 26 deletions(-) diff --git a/shell/platform/windows/keyboard_key_channel_handler.cc b/shell/platform/windows/keyboard_key_channel_handler.cc index 4c127678926f1..15afe1132c0e8 100644 --- a/shell/platform/windows/keyboard_key_channel_handler.cc +++ b/shell/platform/windows/keyboard_key_channel_handler.cc @@ -100,7 +100,7 @@ KeyboardKeyChannelHandler::KeyboardKeyChannelHandler( KeyboardKeyChannelHandler::~KeyboardKeyChannelHandler() = default; -void KeyboardKeyChannelHandler::KeyboardHook(int key, +bool KeyboardKeyChannelHandler::KeyboardHook(int key, int scancode, int action, char32_t character, @@ -126,7 +126,7 @@ void KeyboardKeyChannelHandler::KeyboardHook(int key, break; default: std::cerr << "Unknown key event action: " << action << std::endl; - return; + return false; } channel_->Send(event, [callback = std::move(callback)]( const uint8_t* reply, size_t reply_size) { @@ -135,6 +135,7 @@ void KeyboardKeyChannelHandler::KeyboardHook(int key, bool handled = (*decoded)[kHandledKey].GetBool(); callback(handled); }); + return true; } } // namespace flutter diff --git a/shell/platform/windows/keyboard_key_channel_handler.h b/shell/platform/windows/keyboard_key_channel_handler.h index 9da385235e1cc..323161798fb5a 100644 --- a/shell/platform/windows/keyboard_key_channel_handler.h +++ b/shell/platform/windows/keyboard_key_channel_handler.h @@ -27,7 +27,7 @@ class KeyboardKeyChannelHandler ~KeyboardKeyChannelHandler(); // |KeyboardKeyHandler::KeyboardKeyHandlerDelegate| - void KeyboardHook(int key, + bool KeyboardHook(int key, int scancode, int action, char32_t character, diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index 434fb0fc1f754..6d26bf3fb580e 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -121,7 +121,7 @@ void KeyboardKeyEmbedderHandler::cacheUtf8String(char32_t character) { strcpy_s(character_cache_, kCharacterCacheSize, Utf8FromUtf16(text).c_str()); } -void KeyboardKeyEmbedderHandler::KeyboardHook( +bool KeyboardKeyEmbedderHandler::KeyboardHook( int key, int scancode, int action, @@ -161,7 +161,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( // as a currently pressed one, usually indicating multiple keyboards are // pressing keys with the same physical key, or the up event was lost // during a loss of focus. The down event is ignored. - return; + return false; } } else { // A normal down event (whether the system event is a repeat or not). @@ -175,7 +175,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( if (last_logical_record == 0) { // The physical key has been released before. It indicates multiple // keyboards pressed keys with the same physical key. Ignore the up event. - return; + return false; } else { // A normal up event. type = kFlutterKeyEventTypeUp; @@ -198,7 +198,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( // presses are considered handled and not sent to Flutter. These events must // be filtered by result_logical_key because the key up event of such // presses uses the "original" logical key. - return; + return false; } FlutterKeyEvent key_data = {}; @@ -231,6 +231,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( sendEvent_(key_data, KeyboardKeyEmbedderHandler::HandleResponse, reinterpret_cast(pending_responses_[response_id].get())); + return true; } void KeyboardKeyEmbedderHandler::HandleResponse( diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index b045a8f31a626..f3c29ccddc0b3 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -44,7 +44,7 @@ class KeyboardKeyEmbedderHandler virtual ~KeyboardKeyEmbedderHandler(); // |KeyboardHandlerBase| - void KeyboardHook(int key, + bool KeyboardHook(int key, int scancode, int action, char32_t character, diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index 266a51858f835..4c6e25020d158 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -151,22 +151,29 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, // therefore we simply assert. assert(delegates_.size() != 0); for (const auto& delegate : delegates_) { - delegate->KeyboardHook(key, scancode, action, character, extended, + bool isAsync = delegate->KeyboardHook(key, scancode, action, character, extended, was_down, [pending_event = &pending_event, this](bool handled) { - pending_event->unreplied -= 1; - pending_event->any_handled = pending_event->any_handled || handled; - if (pending_event->unreplied == 0) { - if (!pending_event->any_handled) { - RedispatchEvent(pending_event); - } else { - RemovePendingEvent(pending_event->id); - } - } + ResolvePendingEvent(pending_event, handled); }); + if (!isAsync) { + ResolvePendingEvent(&pending_event, false); + } } return true; } +void KeyboardKeyHandler::ResolvePendingEvent(PendingEvent* pending_event, bool handled) { + pending_event->unreplied -= 1; + pending_event->any_handled = pending_event->any_handled || handled; + if (pending_event->unreplied == 0) { + if (!pending_event->any_handled) { + RedispatchEvent(pending_event); + } else { + RemovePendingEvent(pending_event->id); + } + } +} + void KeyboardKeyHandler::ComposeBeginHook() { // Ignore. } diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index eefc724655424..f843f89ba9ba1 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -26,7 +26,9 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { public: class KeyboardKeyHandlerDelegate { public: - virtual void KeyboardHook(int key, + // Returns true if the delegate is waiting for an async response, + // in which case the `callback` should not be called. + virtual bool KeyboardHook(int key, int scancode, int action, char32_t character, @@ -88,6 +90,7 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { int scancode, int character); void RedispatchEvent(const PendingEvent* pending); + void ResolvePendingEvent(PendingEvent* pending, bool handled); std::vector> delegates_; diff --git a/shell/platform/windows/keyboard_key_handler_unittests.cc b/shell/platform/windows/keyboard_key_handler_unittests.cc index 9da4bb1efeebf..320dffd1c0c9b 100644 --- a/shell/platform/windows/keyboard_key_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_handler_unittests.cc @@ -38,7 +38,7 @@ class MockKeyHandlerDelegate : delegate_id(delegate_id), hook_history(hook_history) {} virtual ~MockKeyHandlerDelegate() = default; - virtual void KeyboardHook(int key, + virtual bool KeyboardHook(int key, int scancode, int action, char32_t character, @@ -54,6 +54,7 @@ class MockKeyHandlerDelegate .was_down = was_down, .callback = std::move(callback), }); + return true; } int delegate_id; diff --git a/shell/platform/windows/win32_window.cc b/shell/platform/windows/win32_window.cc index 89d8bd9d3f5eb..4dd51f41a3741 100644 --- a/shell/platform/windows/win32_window.cc +++ b/shell/platform/windows/win32_window.cc @@ -382,12 +382,6 @@ Win32Window::HandleMessage(UINT const message, break; } unsigned int keyCode(wparam); - if (keyCode == kImeComposingKeyCode) { - // This is an IME composing mode keypress that will be handled via - // WM_IME_* messages, which update the framework via updates to the text - // and composing range in text editing update messages. - break; - } const unsigned int scancode = (lparam >> 16) & 0xff; const bool extended = ((lparam >> 24) & 0x01) == 0x01; // If the key is a modifier, get its side. From 018ecd2775601c34a065a4258379cd506d1b0d53 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 8 Feb 2021 17:02:34 -0800 Subject: [PATCH 46/82] Some docs --- .../platform/windows/flutter_windows_view.cc | 28 +++++---- shell/platform/windows/flutter_windows_view.h | 4 +- .../platform/windows/keyboard_handler_base.h | 7 ++- .../windows/keyboard_key_channel_handler.h | 4 +- .../windows/keyboard_key_embedder_handler.h | 1 - .../platform/windows/keyboard_key_handler.cc | 12 ++-- shell/platform/windows/keyboard_key_handler.h | 58 +++++++++++++++---- .../windows/win32_flutter_window_unittests.cc | 6 +- 8 files changed, 82 insertions(+), 38 deletions(-) diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 26f34d4e6a8bf..6151e294c1aed 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -70,7 +70,15 @@ void FlutterWindowsView::SetEngine( void FlutterWindowsView::RegisterKeyboardHandlers( flutter::BinaryMessenger* messenger) { - auto key_handler = std::make_unique(); + // There must be only one handler that receives |SendInput|, i.e. only one + // handler that might redispatch events. (See the documentation of + // |KeyboardKeyHandler| to learn about redispatching.) + // + // This is whether an event is a redispatched event is decided by calculating + // the hash of the event. In order to allow the same real event in the future, + // the handler is "toggled" when events pass through, which means this logic + // does not apply when there are more than 1 handler. + auto key_handler = std::make_unique(SendInput); key_handler->AddDelegate(std::make_unique( messenger)); key_handler->AddDelegate(std::make_unique( @@ -79,14 +87,14 @@ void FlutterWindowsView::RegisterKeyboardHandlers( void* user_data) { return engine_->SendKeyEvent(event, callback, user_data); })); - AddKeyboardHandlerBase(std::move(key_handler)); - AddKeyboardHandlerBase( + AddKeyboardHandler(std::move(key_handler)); + AddKeyboardHandler( std::make_unique(messenger, this)); } -void FlutterWindowsView::AddKeyboardHandlerBase( +void FlutterWindowsView::AddKeyboardHandler( std::unique_ptr handler) { - keyboard_hook_handlers_.push_back(std::move(handler)); + keyboard_handlers_.push_back(std::move(handler)); } uint32_t FlutterWindowsView::GetFrameBufferId(size_t width, size_t height) { @@ -284,7 +292,7 @@ void FlutterWindowsView::SendPointerLeave() { } void FlutterWindowsView::SendText(const std::u16string& text) { - for (const auto& handler : keyboard_hook_handlers_) { + for (const auto& handler : keyboard_handlers_) { handler->TextHook(this, text); } } @@ -295,7 +303,7 @@ bool FlutterWindowsView::SendKey(int key, char32_t character, bool extended, bool was_down) { - for (const auto& handler : keyboard_hook_handlers_) { + for (const auto& handler : keyboard_handlers_) { if (handler->KeyboardHook(this, key, scancode, action, character, extended, was_down)) { // key event was handled, so don't send to other handlers. @@ -306,20 +314,20 @@ bool FlutterWindowsView::SendKey(int key, } void FlutterWindowsView::SendComposeBegin() { - for (const auto& handler : keyboard_hook_handlers_) { + for (const auto& handler : keyboard_handlers_) { handler->ComposeBeginHook(); } } void FlutterWindowsView::SendComposeEnd() { - for (const auto& handler : keyboard_hook_handlers_) { + for (const auto& handler : keyboard_handlers_) { handler->ComposeEndHook(); } } void FlutterWindowsView::SendComposeChange(const std::u16string& text, int cursor_pos) { - for (const auto& handler : keyboard_hook_handlers_) { + for (const auto& handler : keyboard_handlers_) { handler->ComposeChangeHook(text, cursor_pos); } } diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index d427f7ab21fe5..30da60270f70b 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -133,7 +133,7 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, flutter::BinaryMessenger* messenger); // Used by RegisterKeyboardHandlers to add a new keyboard hook handler. - void AddKeyboardHandlerBase( + void AddKeyboardHandler( std::unique_ptr handler); private: @@ -270,7 +270,7 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, // Handlers for keyboard events from Windows. std::vector> - keyboard_hook_handlers_; + keyboard_handlers_; // Handler for the flutter/platform channel. std::unique_ptr platform_handler_; diff --git a/shell/platform/windows/keyboard_handler_base.h b/shell/platform/windows/keyboard_handler_base.h index 546af41878e9d..44e91bc29c492 100644 --- a/shell/platform/windows/keyboard_handler_base.h +++ b/shell/platform/windows/keyboard_handler_base.h @@ -13,7 +13,12 @@ namespace flutter { class FlutterWindowsView; -// Abstract class for handling keyboard input events. +// Interface for classes that handles keyboard input events. +// +// Keyboard handlers are added to |FlutterWindowsView| in a chain. +// When a key event arrives, |KeyboardHook| is called on each handler +// until the first one that returns true. Then the proper text hooks +// are called on each handler. class KeyboardHandlerBase { public: virtual ~KeyboardHandlerBase() = default; diff --git a/shell/platform/windows/keyboard_key_channel_handler.h b/shell/platform/windows/keyboard_key_channel_handler.h index 323161798fb5a..86ee9e47337ce 100644 --- a/shell/platform/windows/keyboard_key_channel_handler.h +++ b/shell/platform/windows/keyboard_key_channel_handler.h @@ -12,13 +12,11 @@ #include "flutter/shell/platform/common/client_wrapper/include/flutter/basic_message_channel.h" #include "flutter/shell/platform/common/client_wrapper/include/flutter/binary_messenger.h" #include "flutter/shell/platform/windows/keyboard_key_handler.h" -#include "flutter/shell/platform/windows/public/flutter_windows.h" #include "rapidjson/document.h" namespace flutter { -class FlutterWindowsView; - +// Handled class KeyboardKeyChannelHandler : public KeyboardKeyHandler::KeyboardKeyHandlerDelegate { public: diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index f3c29ccddc0b3..5e8c717b0ebfd 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -12,7 +12,6 @@ #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/windows/keyboard_key_handler.h" -#include "flutter/shell/platform/windows/public/flutter_windows.h" namespace flutter { diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index 4c6e25020d158..341a7776b3b3e 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -56,9 +56,9 @@ uint64_t CalculateEventId(int scancode, int action, bool extended) { KeyboardKeyHandler::KeyboardKeyHandlerDelegate::~KeyboardKeyHandlerDelegate() = default; -KeyboardKeyHandler::KeyboardKeyHandler(SendInputDelegate send_input) - : send_input_(send_input) { - assert(send_input != nullptr); +KeyboardKeyHandler::KeyboardKeyHandler(RedispatchEvent redispatch_event) + : redispatch_event_(redispatch_event) { + assert(redispatch_event_ != nullptr); } KeyboardKeyHandler::~KeyboardKeyHandler() = default; @@ -96,7 +96,7 @@ void KeyboardKeyHandler::RemovePendingEvent(uint64_t id) { << ", but the event was not found." << std::endl; } -void KeyboardKeyHandler::RedispatchEvent(const PendingEvent* pending) { +void KeyboardKeyHandler::DoRedispatchEvent(const PendingEvent* pending) { KEYBDINPUT ki{ .wVk = 0, .wScan = static_cast(pending->scancode), @@ -105,7 +105,7 @@ void KeyboardKeyHandler::RedispatchEvent(const PendingEvent* pending) { INPUT input_event; input_event.type = INPUT_KEYBOARD; input_event.ki = std::move(ki); - UINT accepted = send_input_(1, &input_event, sizeof(input_event)); + UINT accepted = redispatch_event_(1, &input_event, sizeof(input_event)); if (accepted != 1) { std::cerr << "Unable to synthesize event for unhandled keyboard event " "with scancode " @@ -167,7 +167,7 @@ void KeyboardKeyHandler::ResolvePendingEvent(PendingEvent* pending_event, bool h pending_event->any_handled = pending_event->any_handled || handled; if (pending_event->unreplied == 0) { if (!pending_event->any_handled) { - RedispatchEvent(pending_event); + DoRedispatchEvent(pending_event); } else { RemovePendingEvent(pending_event->id); } diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index f843f89ba9ba1..71f393e167ce0 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -19,15 +19,29 @@ namespace flutter { class FlutterWindowsView; -// Implements a KeyboardHandlerBase +// Handles key events. // -// Handles key events and forwards them to the Flutter engine. +// This class detects whether an incoming event is a redispatched one, +// dispatches native events to delegates and collect their responses, +// and redispatches events unhandled by Flutter back to the system. +// See |KeyboardHook| for more information. +// +// The exact behavior to handle events are further divided into delegates. +// See |KeyboardKeyHandlerDelegate| and its subclasses. class KeyboardKeyHandler : public KeyboardHandlerBase { public: + // An interface for concrete definition of how to asynchronously handle key + // events. class KeyboardKeyHandlerDelegate { public: - // Returns true if the delegate is waiting for an async response, - // in which case the `callback` should not be called. + // Defines how to how to asynchronously handle key events. + // + // If the delegate will wait for an asynchronous response for this event, + // |KeyboardHook| should return true, and invoke |callback| with the + // response (whether the event is handled) later for exactly once. + // + // Otherwise, the delegate should return false and *never* invoke + // |callback|. This is considered not handling the event. virtual bool KeyboardHook(int key, int scancode, int action, @@ -39,16 +53,38 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { virtual ~KeyboardKeyHandlerDelegate(); }; - using SendInputDelegate = + using RedispatchEvent = std::function; - explicit KeyboardKeyHandler(SendInputDelegate delegate = SendInput); + // Create a KeyboardKeyHandler and specify where to redispatch events. + // + // The |redispatch_event| is typically |SendInput|. + explicit KeyboardKeyHandler(RedispatchEvent redispatch_event); ~KeyboardKeyHandler(); + // Add a delegate that handles events received by |KeyboardHook|. void AddDelegate(std::unique_ptr delegate); - // |KeyboardHandlerBase| + // Handles a key event. + // + // Returns whether this handler claims to handle the event, which is true if + // the event is a native event, or false if the event is a redispatched one. + // + // Windows requires a synchronous response of whether a key event should be + // handled, while the query to Flutter is always asynchronous. This is resolved + // by "redispatching": the response to the native event is always true. If + // Flutter later decides not to handle the event, an event is then synthesized, + // dispatched to system, received again, detected, at which time |KeyboardHook| + // returns false, then falls back to other keyboard handlers. + // + // Received events are further dispatched to all added delegates. If any + // delegate returns true (handled), the event is considered handled. When + // all delegates responded, any unhandled events are redispatched via + // |redispatch_event| and recorded. The next (one) time this exact event is + // received, |KeyboardHook| will skip it and immediately return false. + // + // Inherited from |KeyboardHandlerBase|. bool KeyboardHook(FlutterWindowsView* window, int key, int scancode, @@ -89,7 +125,7 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { bool extended, int scancode, int character); - void RedispatchEvent(const PendingEvent* pending); + void DoRedispatchEvent(const PendingEvent* pending); void ResolvePendingEvent(PendingEvent* pending, bool handled); std::vector> delegates_; @@ -98,10 +134,8 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { // yet received a response. std::deque> pending_events_; - // A function used to dispatch synthesized events. Used in testing to inject a - // test function to collect events. Defaults to the Windows function - // SendInput. - SendInputDelegate send_input_; + // A function used to redispatch synthesized events. + RedispatchEvent redispatch_event_; }; } // namespace flutter diff --git a/shell/platform/windows/win32_flutter_window_unittests.cc b/shell/platform/windows/win32_flutter_window_unittests.cc index 3dbcd57e001d2..11db83c7a59d9 100644 --- a/shell/platform/windows/win32_flutter_window_unittests.cc +++ b/shell/platform/windows/win32_flutter_window_unittests.cc @@ -52,7 +52,7 @@ struct SimulatedEvent { class SpyKeyboardKeyHandler : public KeyboardHandlerBase { public: SpyKeyboardKeyHandler(flutter::BinaryMessenger* messenger, - KeyboardKeyHandler::SendInputDelegate delegate) { + KeyboardKeyHandler::RedispatchEvent delegate) { real_implementation_ = std::make_unique(delegate); real_implementation_->AddDelegate( @@ -183,8 +183,8 @@ class TestFlutterWindowsView : public FlutterWindowsView { std::make_unique(messenger); key_event_handler = spy_key_event_handler.get(); text_input_plugin = spy_text_input_plugin.get(); - AddKeyboardHandlerBase(std::move(spy_key_event_handler)); - AddKeyboardHandlerBase(std::move(spy_text_input_plugin)); + AddKeyboardHandler(std::move(spy_key_event_handler)); + AddKeyboardHandler(std::move(spy_text_input_plugin)); } private: From 7647c39d3d3eec40b857ca9bc0a980b51c73c688 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 8 Feb 2021 22:20:52 -0800 Subject: [PATCH 47/82] More docs --- .../windows/keyboard_key_channel_handler.h | 7 ++++++- .../windows/keyboard_key_embedder_handler.h | 15 ++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/shell/platform/windows/keyboard_key_channel_handler.h b/shell/platform/windows/keyboard_key_channel_handler.h index 86ee9e47337ce..f2c102d70638f 100644 --- a/shell/platform/windows/keyboard_key_channel_handler.h +++ b/shell/platform/windows/keyboard_key_channel_handler.h @@ -16,10 +16,15 @@ namespace flutter { -// Handled +// A delegate of |KeyboardKeyHandler| that handles events by sending the +// raw information through the method channel. +// +// This class corresponds to the RawKeyboard API in the framework. class KeyboardKeyChannelHandler : public KeyboardKeyHandler::KeyboardKeyHandlerDelegate { public: + // Create a |KeyboardKeyChannelHandler| by specifying the messenger + // through which the events are sent. explicit KeyboardKeyChannelHandler(flutter::BinaryMessenger* messenger); ~KeyboardKeyChannelHandler(); diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index 5e8c717b0ebfd..795b2832ab457 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -23,22 +23,23 @@ namespace { constexpr size_t kCharacterCacheSize = 8; } // namespace -// Processes native keyboard events. +// A delegate of |KeyboardKeyHandler| that handles events by sending +// converted |FlutterKeyEvent|s through the embedder API. // -// This class converts received keyboard events to Flutter events, -// sends them to the engine, waits for response. +// This class corresponds to the HardwareKeyboard API in the framework. class KeyboardKeyEmbedderHandler : public KeyboardKeyHandler::KeyboardKeyHandlerDelegate { public: + using SendEvent = std::function; + // Build a KeyboardKeyEmbedderHandler. // // Use `send_event` to define how the manager should dispatch converted // flutter events, as well as how to receive the resopnse, to the engine. It's // typically FlutterWindowsEngine::SendKeyEvent. - explicit KeyboardKeyEmbedderHandler( - std::function send_event); + explicit KeyboardKeyEmbedderHandler(SendEvent send_event); virtual ~KeyboardKeyEmbedderHandler(); From e4bee5ec9bbb6568674f4e5429b3dc8d52f77ea7 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 8 Feb 2021 23:27:34 -0800 Subject: [PATCH 48/82] Fix license --- shell/platform/windows/flutter_key_map.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/windows/flutter_key_map.cc b/shell/platform/windows/flutter_key_map.cc index c5db8db546a49..4a36a13fe4dcd 100644 --- a/shell/platform/windows/flutter_key_map.cc +++ b/shell/platform/windows/flutter_key_map.cc @@ -1,4 +1,4 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. +// 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. From e7a736fec0cef67f8fb63454d5c594ec86306af7 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 8 Feb 2021 23:48:19 -0800 Subject: [PATCH 49/82] Fix format --- shell/platform/windows/BUILD.gn | 6 +- shell/platform/windows/flutter_key_map.cc | 595 +++++++++--------- .../platform/windows/flutter_windows_view.cc | 15 +- shell/platform/windows/flutter_windows_view.h | 6 +- .../windows/keyboard_key_channel_handler.cc | 19 +- .../keyboard_key_channel_handler_unittests.cc | 24 +- .../windows/keyboard_key_embedder_handler.cc | 39 +- .../windows/keyboard_key_embedder_handler.h | 5 +- ...keyboard_key_embedder_handler_unittests.cc | 52 +- .../platform/windows/keyboard_key_handler.cc | 59 +- shell/platform/windows/keyboard_key_handler.h | 10 +- .../windows/keyboard_key_handler_unittests.cc | 78 +-- .../windows/win32_flutter_window_unittests.cc | 22 +- 13 files changed, 469 insertions(+), 461 deletions(-) diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index c5122027bcd1c..7c6c68dec5313 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -60,10 +60,10 @@ source_set("flutter_windows_source") { "flutter_windows_view.cc", "flutter_windows_view.h", "keyboard_handler_base.h", - "keyboard_key_embedder_handler.cc", - "keyboard_key_embedder_handler.h", "keyboard_key_channel_handler.cc", "keyboard_key_channel_handler.h", + "keyboard_key_embedder_handler.cc", + "keyboard_key_embedder_handler.h", "keyboard_key_handler.cc", "keyboard_key_handler.h", "platform_handler.cc", @@ -194,8 +194,8 @@ executable("flutter_windows_unittests") { "flutter_project_bundle_unittests.cc", "flutter_windows_engine_unittests.cc", "flutter_windows_texture_registrar_unittests.cc", - "keyboard_key_embedder_handler_unittests.cc", "keyboard_key_channel_handler_unittests.cc", + "keyboard_key_embedder_handler_unittests.cc", "keyboard_key_handler_unittests.cc", "string_conversion_unittests.cc", "system_utils_unittests.cc", diff --git a/shell/platform/windows/flutter_key_map.cc b/shell/platform/windows/flutter_key_map.cc index 4a36a13fe4dcd..69256a78f2606 100644 --- a/shell/platform/windows/flutter_key_map.cc +++ b/shell/platform/windows/flutter_key_map.cc @@ -19,306 +19,309 @@ namespace flutter { -std::map KeyboardKeyEmbedderHandler::windowsToPhysicalMap_ = { - {0x0000e05f, 0x00010082}, // sleep - {0x0000e063, 0x00010083}, // wakeUp - {0x000000ff, 0x00070001}, // usbErrorRollOver - {0x000000fc, 0x00070002}, // usbPostFail - {0x0000001e, 0x00070004}, // keyA - {0x00000030, 0x00070005}, // keyB - {0x0000002e, 0x00070006}, // keyC - {0x00000020, 0x00070007}, // keyD - {0x00000012, 0x00070008}, // keyE - {0x00000021, 0x00070009}, // keyF - {0x00000022, 0x0007000a}, // keyG - {0x00000023, 0x0007000b}, // keyH - {0x00000017, 0x0007000c}, // keyI - {0x00000024, 0x0007000d}, // keyJ - {0x00000025, 0x0007000e}, // keyK - {0x00000026, 0x0007000f}, // keyL - {0x00000032, 0x00070010}, // keyM - {0x00000031, 0x00070011}, // keyN - {0x00000018, 0x00070012}, // keyO - {0x00000019, 0x00070013}, // keyP - {0x00000010, 0x00070014}, // keyQ - {0x00000013, 0x00070015}, // keyR - {0x0000001f, 0x00070016}, // keyS - {0x00000014, 0x00070017}, // keyT - {0x00000016, 0x00070018}, // keyU - {0x0000002f, 0x00070019}, // keyV - {0x00000011, 0x0007001a}, // keyW - {0x0000002d, 0x0007001b}, // keyX - {0x00000015, 0x0007001c}, // keyY - {0x0000002c, 0x0007001d}, // keyZ - {0x00000002, 0x0007001e}, // digit1 - {0x00000003, 0x0007001f}, // digit2 - {0x00000004, 0x00070020}, // digit3 - {0x00000005, 0x00070021}, // digit4 - {0x00000006, 0x00070022}, // digit5 - {0x00000007, 0x00070023}, // digit6 - {0x00000008, 0x00070024}, // digit7 - {0x00000009, 0x00070025}, // digit8 - {0x0000000a, 0x00070026}, // digit9 - {0x0000000b, 0x00070027}, // digit0 - {0x0000001c, 0x00070028}, // enter - {0x00000001, 0x00070029}, // escape - {0x0000000e, 0x0007002a}, // backspace - {0x0000000f, 0x0007002b}, // tab - {0x00000039, 0x0007002c}, // space - {0x0000000c, 0x0007002d}, // minus - {0x0000000d, 0x0007002e}, // equal - {0x0000001a, 0x0007002f}, // bracketLeft - {0x0000001b, 0x00070030}, // bracketRight - {0x0000002b, 0x00070031}, // backslash - {0x00000027, 0x00070033}, // semicolon - {0x00000028, 0x00070034}, // quote - {0x00000029, 0x00070035}, // backquote - {0x00000033, 0x00070036}, // comma - {0x00000034, 0x00070037}, // period - {0x00000035, 0x00070038}, // slash - {0x0000003a, 0x00070039}, // capsLock - {0x0000003b, 0x0007003a}, // f1 - {0x0000003c, 0x0007003b}, // f2 - {0x0000003d, 0x0007003c}, // f3 - {0x0000003e, 0x0007003d}, // f4 - {0x0000003f, 0x0007003e}, // f5 - {0x00000040, 0x0007003f}, // f6 - {0x00000041, 0x00070040}, // f7 - {0x00000042, 0x00070041}, // f8 - {0x00000043, 0x00070042}, // f9 - {0x00000044, 0x00070043}, // f10 - {0x00000057, 0x00070044}, // f11 - {0x00000058, 0x00070045}, // f12 - {0x0000e037, 0x00070046}, // printScreen - {0x00000046, 0x00070047}, // scrollLock - {0x00000045, 0x00070048}, // pause - {0x0000e052, 0x00070049}, // insert - {0x0000e047, 0x0007004a}, // home - {0x0000e049, 0x0007004b}, // pageUp - {0x0000e053, 0x0007004c}, // delete - {0x0000e04f, 0x0007004d}, // end - {0x0000e051, 0x0007004e}, // pageDown - {0x0000e04d, 0x0007004f}, // arrowRight - {0x0000e04b, 0x00070050}, // arrowLeft - {0x0000e050, 0x00070051}, // arrowDown - {0x0000e048, 0x00070052}, // arrowUp - {0x0000e045, 0x00070053}, // numLock - {0x0000e035, 0x00070054}, // numpadDivide - {0x00000037, 0x00070055}, // numpadMultiply - {0x0000004a, 0x00070056}, // numpadSubtract - {0x0000004e, 0x00070057}, // numpadAdd - {0x0000e01c, 0x00070058}, // numpadEnter - {0x0000004f, 0x00070059}, // numpad1 - {0x00000050, 0x0007005a}, // numpad2 - {0x00000051, 0x0007005b}, // numpad3 - {0x0000004b, 0x0007005c}, // numpad4 - {0x0000004c, 0x0007005d}, // numpad5 - {0x0000004d, 0x0007005e}, // numpad6 - {0x00000047, 0x0007005f}, // numpad7 - {0x00000048, 0x00070060}, // numpad8 - {0x00000049, 0x00070061}, // numpad9 - {0x00000052, 0x00070062}, // numpad0 - {0x00000053, 0x00070063}, // numpadDecimal - {0x00000056, 0x00070064}, // intlBackslash - {0x0000e05d, 0x00070065}, // contextMenu - {0x0000e05e, 0x00070066}, // power - {0x00000059, 0x00070067}, // numpadEqual - {0x00000064, 0x00070068}, // f13 - {0x00000065, 0x00070069}, // f14 - {0x00000066, 0x0007006a}, // f15 - {0x00000067, 0x0007006b}, // f16 - {0x00000068, 0x0007006c}, // f17 - {0x00000069, 0x0007006d}, // f18 - {0x0000006a, 0x0007006e}, // f19 - {0x0000006b, 0x0007006f}, // f20 - {0x0000006c, 0x00070070}, // f21 - {0x0000006d, 0x00070071}, // f22 - {0x0000006e, 0x00070072}, // f23 - {0x00000076, 0x00070073}, // f24 - {0x0000e03b, 0x00070075}, // help - {0x0000e008, 0x0007007a}, // undo - {0x0000e017, 0x0007007b}, // cut - {0x0000e018, 0x0007007c}, // copy - {0x0000e00a, 0x0007007d}, // paste - {0x0000e020, 0x0007007f}, // audioVolumeMute - {0x0000e030, 0x00070080}, // audioVolumeUp - {0x0000e02e, 0x00070081}, // audioVolumeDown - {0x0000007e, 0x00070085}, // numpadComma - {0x00000073, 0x00070087}, // intlRo - {0x00000070, 0x00070088}, // kanaMode - {0x0000007d, 0x00070089}, // intlYen - {0x00000079, 0x0007008a}, // convert - {0x0000007b, 0x0007008b}, // nonConvert - {0x00000072, 0x00070090}, // lang1 - {0x00000071, 0x00070091}, // lang2 - {0x00000078, 0x00070092}, // lang3 - {0x00000077, 0x00070093}, // lang4 - {0x0000001d, 0x000700e0}, // controlLeft - {0x0000002a, 0x000700e1}, // shiftLeft - {0x00000038, 0x000700e2}, // altLeft - {0x0000e05b, 0x000700e3}, // metaLeft - {0x0000e01d, 0x000700e4}, // controlRight - {0x00000036, 0x000700e5}, // shiftRight - {0x0000e038, 0x000700e6}, // altRight - {0x0000e05c, 0x000700e7}, // metaRight - {0x0000e019, 0x000c00b5}, // mediaTrackNext - {0x0000e010, 0x000c00b6}, // mediaTrackPrevious - {0x0000e024, 0x000c00b7}, // mediaStop - {0x0000e02c, 0x000c00b8}, // eject - {0x0000e022, 0x000c00cd}, // mediaPlayPause - {0x0000e06d, 0x000c0183}, // mediaSelect - {0x0000e06c, 0x000c018a}, // launchMail - {0x0000e021, 0x000c0192}, // launchApp2 - {0x0000e06b, 0x000c0194}, // launchApp1 - {0x0000e065, 0x000c0221}, // browserSearch - {0x0000e032, 0x000c0223}, // browserHome - {0x0000e06a, 0x000c0224}, // browserBack - {0x0000e069, 0x000c0225}, // browserForward - {0x0000e068, 0x000c0226}, // browserStop - {0x0000e067, 0x000c0227}, // browserRefresh - {0x0000e066, 0x000c022a}, // browserFavorites +std::map KeyboardKeyEmbedderHandler::windowsToPhysicalMap_ = + { + {0x0000e05f, 0x00010082}, // sleep + {0x0000e063, 0x00010083}, // wakeUp + {0x000000ff, 0x00070001}, // usbErrorRollOver + {0x000000fc, 0x00070002}, // usbPostFail + {0x0000001e, 0x00070004}, // keyA + {0x00000030, 0x00070005}, // keyB + {0x0000002e, 0x00070006}, // keyC + {0x00000020, 0x00070007}, // keyD + {0x00000012, 0x00070008}, // keyE + {0x00000021, 0x00070009}, // keyF + {0x00000022, 0x0007000a}, // keyG + {0x00000023, 0x0007000b}, // keyH + {0x00000017, 0x0007000c}, // keyI + {0x00000024, 0x0007000d}, // keyJ + {0x00000025, 0x0007000e}, // keyK + {0x00000026, 0x0007000f}, // keyL + {0x00000032, 0x00070010}, // keyM + {0x00000031, 0x00070011}, // keyN + {0x00000018, 0x00070012}, // keyO + {0x00000019, 0x00070013}, // keyP + {0x00000010, 0x00070014}, // keyQ + {0x00000013, 0x00070015}, // keyR + {0x0000001f, 0x00070016}, // keyS + {0x00000014, 0x00070017}, // keyT + {0x00000016, 0x00070018}, // keyU + {0x0000002f, 0x00070019}, // keyV + {0x00000011, 0x0007001a}, // keyW + {0x0000002d, 0x0007001b}, // keyX + {0x00000015, 0x0007001c}, // keyY + {0x0000002c, 0x0007001d}, // keyZ + {0x00000002, 0x0007001e}, // digit1 + {0x00000003, 0x0007001f}, // digit2 + {0x00000004, 0x00070020}, // digit3 + {0x00000005, 0x00070021}, // digit4 + {0x00000006, 0x00070022}, // digit5 + {0x00000007, 0x00070023}, // digit6 + {0x00000008, 0x00070024}, // digit7 + {0x00000009, 0x00070025}, // digit8 + {0x0000000a, 0x00070026}, // digit9 + {0x0000000b, 0x00070027}, // digit0 + {0x0000001c, 0x00070028}, // enter + {0x00000001, 0x00070029}, // escape + {0x0000000e, 0x0007002a}, // backspace + {0x0000000f, 0x0007002b}, // tab + {0x00000039, 0x0007002c}, // space + {0x0000000c, 0x0007002d}, // minus + {0x0000000d, 0x0007002e}, // equal + {0x0000001a, 0x0007002f}, // bracketLeft + {0x0000001b, 0x00070030}, // bracketRight + {0x0000002b, 0x00070031}, // backslash + {0x00000027, 0x00070033}, // semicolon + {0x00000028, 0x00070034}, // quote + {0x00000029, 0x00070035}, // backquote + {0x00000033, 0x00070036}, // comma + {0x00000034, 0x00070037}, // period + {0x00000035, 0x00070038}, // slash + {0x0000003a, 0x00070039}, // capsLock + {0x0000003b, 0x0007003a}, // f1 + {0x0000003c, 0x0007003b}, // f2 + {0x0000003d, 0x0007003c}, // f3 + {0x0000003e, 0x0007003d}, // f4 + {0x0000003f, 0x0007003e}, // f5 + {0x00000040, 0x0007003f}, // f6 + {0x00000041, 0x00070040}, // f7 + {0x00000042, 0x00070041}, // f8 + {0x00000043, 0x00070042}, // f9 + {0x00000044, 0x00070043}, // f10 + {0x00000057, 0x00070044}, // f11 + {0x00000058, 0x00070045}, // f12 + {0x0000e037, 0x00070046}, // printScreen + {0x00000046, 0x00070047}, // scrollLock + {0x00000045, 0x00070048}, // pause + {0x0000e052, 0x00070049}, // insert + {0x0000e047, 0x0007004a}, // home + {0x0000e049, 0x0007004b}, // pageUp + {0x0000e053, 0x0007004c}, // delete + {0x0000e04f, 0x0007004d}, // end + {0x0000e051, 0x0007004e}, // pageDown + {0x0000e04d, 0x0007004f}, // arrowRight + {0x0000e04b, 0x00070050}, // arrowLeft + {0x0000e050, 0x00070051}, // arrowDown + {0x0000e048, 0x00070052}, // arrowUp + {0x0000e045, 0x00070053}, // numLock + {0x0000e035, 0x00070054}, // numpadDivide + {0x00000037, 0x00070055}, // numpadMultiply + {0x0000004a, 0x00070056}, // numpadSubtract + {0x0000004e, 0x00070057}, // numpadAdd + {0x0000e01c, 0x00070058}, // numpadEnter + {0x0000004f, 0x00070059}, // numpad1 + {0x00000050, 0x0007005a}, // numpad2 + {0x00000051, 0x0007005b}, // numpad3 + {0x0000004b, 0x0007005c}, // numpad4 + {0x0000004c, 0x0007005d}, // numpad5 + {0x0000004d, 0x0007005e}, // numpad6 + {0x00000047, 0x0007005f}, // numpad7 + {0x00000048, 0x00070060}, // numpad8 + {0x00000049, 0x00070061}, // numpad9 + {0x00000052, 0x00070062}, // numpad0 + {0x00000053, 0x00070063}, // numpadDecimal + {0x00000056, 0x00070064}, // intlBackslash + {0x0000e05d, 0x00070065}, // contextMenu + {0x0000e05e, 0x00070066}, // power + {0x00000059, 0x00070067}, // numpadEqual + {0x00000064, 0x00070068}, // f13 + {0x00000065, 0x00070069}, // f14 + {0x00000066, 0x0007006a}, // f15 + {0x00000067, 0x0007006b}, // f16 + {0x00000068, 0x0007006c}, // f17 + {0x00000069, 0x0007006d}, // f18 + {0x0000006a, 0x0007006e}, // f19 + {0x0000006b, 0x0007006f}, // f20 + {0x0000006c, 0x00070070}, // f21 + {0x0000006d, 0x00070071}, // f22 + {0x0000006e, 0x00070072}, // f23 + {0x00000076, 0x00070073}, // f24 + {0x0000e03b, 0x00070075}, // help + {0x0000e008, 0x0007007a}, // undo + {0x0000e017, 0x0007007b}, // cut + {0x0000e018, 0x0007007c}, // copy + {0x0000e00a, 0x0007007d}, // paste + {0x0000e020, 0x0007007f}, // audioVolumeMute + {0x0000e030, 0x00070080}, // audioVolumeUp + {0x0000e02e, 0x00070081}, // audioVolumeDown + {0x0000007e, 0x00070085}, // numpadComma + {0x00000073, 0x00070087}, // intlRo + {0x00000070, 0x00070088}, // kanaMode + {0x0000007d, 0x00070089}, // intlYen + {0x00000079, 0x0007008a}, // convert + {0x0000007b, 0x0007008b}, // nonConvert + {0x00000072, 0x00070090}, // lang1 + {0x00000071, 0x00070091}, // lang2 + {0x00000078, 0x00070092}, // lang3 + {0x00000077, 0x00070093}, // lang4 + {0x0000001d, 0x000700e0}, // controlLeft + {0x0000002a, 0x000700e1}, // shiftLeft + {0x00000038, 0x000700e2}, // altLeft + {0x0000e05b, 0x000700e3}, // metaLeft + {0x0000e01d, 0x000700e4}, // controlRight + {0x00000036, 0x000700e5}, // shiftRight + {0x0000e038, 0x000700e6}, // altRight + {0x0000e05c, 0x000700e7}, // metaRight + {0x0000e019, 0x000c00b5}, // mediaTrackNext + {0x0000e010, 0x000c00b6}, // mediaTrackPrevious + {0x0000e024, 0x000c00b7}, // mediaStop + {0x0000e02c, 0x000c00b8}, // eject + {0x0000e022, 0x000c00cd}, // mediaPlayPause + {0x0000e06d, 0x000c0183}, // mediaSelect + {0x0000e06c, 0x000c018a}, // launchMail + {0x0000e021, 0x000c0192}, // launchApp2 + {0x0000e06b, 0x000c0194}, // launchApp1 + {0x0000e065, 0x000c0221}, // browserSearch + {0x0000e032, 0x000c0223}, // browserHome + {0x0000e06a, 0x000c0224}, // browserBack + {0x0000e069, 0x000c0225}, // browserForward + {0x0000e068, 0x000c0226}, // browserStop + {0x0000e067, 0x000c0227}, // browserRefresh + {0x0000e066, 0x000c022a}, // browserFavorites }; -std::map KeyboardKeyEmbedderHandler::windowsToLogicalMap_ = { - {0x00000008, 0x00000000008}, // BACK - {0x00000009, 0x00000000009}, // TAB - {0x0000000d, 0x0000000000d}, // RETURN - {0x0000001b, 0x0000000001b}, // ESCAPE - {0x00000020, 0x00000000020}, // SPACE - {0x000000de, 0x00000000022}, // OEM_7 - {0x000000bc, 0x0000000002c}, // OEM_COMMA - {0x000000bd, 0x0000000002d}, // OEM_MINUS - {0x000000be, 0x0000000002e}, // OEM_PERIOD - {0x000000bf, 0x0000000002f}, // OEM_2 - {0x000000ba, 0x0000000003b}, // OEM_1 - {0x000000bb, 0x0000000003d}, // OEM_PLUS - {0x000000db, 0x0000000005b}, // OEM_4 - {0x000000dc, 0x0000000005c}, // OEM_5 - {0x000000dd, 0x0000000005d}, // OEM_6 - {0x000000c0, 0x00000000060}, // OEM_3 - {0x0000002e, 0x0000000007f}, // DELETE - {0x00000014, 0x00000000104}, // CAPITAL - {0x00000090, 0x0000000010a}, // NUMLOCK - {0x00000091, 0x0000000010c}, // SCROLL - {0x00000028, 0x00000000301}, // DOWN - {0x00000025, 0x00000000302}, // LEFT - {0x00000027, 0x00000000303}, // RIGHT - {0x00000026, 0x00000000304}, // UP - {0x00000023, 0x00000000305}, // END - {0x00000024, 0x00000000306}, // HOME - {0x00000022, 0x00000000307}, // NEXT - {0x00000021, 0x00000000308}, // PRIOR - {0x0000000c, 0x00000000401}, // CLEAR - {0x0000002d, 0x00000000407}, // INSERT - {0x0000001e, 0x00000000501}, // ACCEPT - {0x000000f6, 0x00000000503}, // ATTN - {0x00000003, 0x00000000504}, // CANCEL - {0x0000005d, 0x00000000505}, // APPS - {0x0000002b, 0x00000000506}, // EXECUTE - {0x0000002f, 0x00000000508}, // HELP - {0x00000013, 0x00000000509}, // PAUSE - {0x000000fa, 0x0000000050a}, // PLAY - {0x00000029, 0x0000000050c}, // SELECT - {0x0000001c, 0x00000000705}, // CONVERT - {0x0000001f, 0x0000000070b}, // MODECHANGE - {0x00000070, 0x00000000801}, // F1 - {0x00000071, 0x00000000802}, // F2 - {0x00000072, 0x00000000803}, // F3 - {0x00000073, 0x00000000804}, // F4 - {0x00000074, 0x00000000805}, // F5 - {0x00000075, 0x00000000806}, // F6 - {0x00000076, 0x00000000807}, // F7 - {0x00000077, 0x00000000808}, // F8 - {0x00000078, 0x00000000809}, // F9 - {0x00000079, 0x0000000080a}, // F10 - {0x0000007a, 0x0000000080b}, // F11 - {0x0000007b, 0x0000000080c}, // F12 - {0x0000007c, 0x0000000080d}, // F13 - {0x0000007d, 0x0000000080e}, // F14 - {0x0000007e, 0x0000000080f}, // F15 - {0x0000007f, 0x00000000810}, // F16 - {0x00000080, 0x00000000811}, // F17 - {0x00000081, 0x00000000812}, // F18 - {0x00000082, 0x00000000813}, // F19 - {0x00000083, 0x00000000814}, // F20 - {0x00000084, 0x00000000815}, // F21 - {0x00000085, 0x00000000816}, // F22 - {0x00000086, 0x00000000817}, // F23 - {0x00000087, 0x00000000818}, // F24 - {0x000000b3, 0x00000000a05}, // MEDIA_PLAY_PAUSE - {0x000000b2, 0x00000000a07}, // MEDIA_STOP - {0x0000002a, 0x00000000a0c}, // PRINT - {0x000000ae, 0x00000000a0f}, // VOLUME_DOWN - {0x000000af, 0x00000000a10}, // VOLUME_UP - {0x000000ad, 0x00000000a11}, // VOLUME_MUTE - {0x000000b4, 0x00000000b03}, // LAUNCH_MAIL - {0x000000a6, 0x00000000c01}, // BROWSER_BACK - {0x000000ab, 0x00000000c02}, // BROWSER_FAVORITES - {0x000000a7, 0x00000000c03}, // BROWSER_FORWARD - {0x000000ac, 0x00000000c04}, // BROWSER_HOME - {0x000000a8, 0x00000000c05}, // BROWSER_REFRESH - {0x000000aa, 0x00000000c06}, // BROWSER_SEARCH - {0x000000a9, 0x00000000c07}, // BROWSER_STOP - {0x000000c3, 0x0000005ff08}, // GAMEPAD_A - {0x000000c4, 0x0000005ff09}, // GAMEPAD_B - {0x000000c5, 0x0000005ff0a}, // GAMEPAD_X - {0x000000c6, 0x0000005ff0b}, // GAMEPAD_Y - {0x000000c7, 0x0000005ff0c}, // GAMEPAD_RIGHT_SHOULDER - {0x000000c8, 0x0000005ff0d}, // GAMEPAD_LEFT_SHOULDER - {0x000000c9, 0x0000005ff0e}, // GAMEPAD_LEFT_TRIGGER - {0x000000ca, 0x0000005ff0f}, // GAMEPAD_RIGHT_TRIGGER - {0x000000cb, 0x0000005ff10}, // GAMEPAD_DPAD_UP - {0x0000005f, 0x00100010082}, // SLEEP - {0x00000015, 0x00100070090}, // KANA - {0x00000015, 0x00100070090}, // HANGEUL - {0x00000015, 0x00100070090}, // HANGUL - {0x0000006a, 0x0020000002a}, // MULTIPLY - {0x0000006b, 0x0020000002b}, // ADD - {0x0000006d, 0x0020000002d}, // SUBTRACT - {0x0000006e, 0x0020000002e}, // DECIMAL - {0x0000006f, 0x0020000002f}, // DIVIDE - {0x00000060, 0x00200000030}, // NUMPAD0 - {0x00000061, 0x00200000031}, // NUMPAD1 - {0x00000062, 0x00200000032}, // NUMPAD2 - {0x00000063, 0x00200000033}, // NUMPAD3 - {0x00000064, 0x00200000034}, // NUMPAD4 - {0x00000065, 0x00200000035}, // NUMPAD5 - {0x00000066, 0x00200000036}, // NUMPAD6 - {0x00000067, 0x00200000037}, // NUMPAD7 - {0x00000068, 0x00200000038}, // NUMPAD8 - {0x00000069, 0x00200000039}, // NUMPAD9 - {0x00000092, 0x0020000003d}, // OEM_NEC_EQUAL - {0x000000a4, 0x00300000102}, // LMENU - {0x00000011, 0x00300000105}, // CONTROL - {0x000000a2, 0x00300000105}, // LCONTROL - {0x0000005b, 0x00300000109}, // LWIN - {0x00000010, 0x0030000010d}, // SHIFT - {0x000000a0, 0x0030000010d}, // LSHIFT - {0x000000a5, 0x00400000102}, // RMENU - {0x000000a3, 0x00400000105}, // RCONTROL - {0x0000005c, 0x00400000109}, // RWIN - {0x000000a1, 0x0040000010d}, // RSHIFT +std::map KeyboardKeyEmbedderHandler::windowsToLogicalMap_ = + { + {0x00000008, 0x00000000008}, // BACK + {0x00000009, 0x00000000009}, // TAB + {0x0000000d, 0x0000000000d}, // RETURN + {0x0000001b, 0x0000000001b}, // ESCAPE + {0x00000020, 0x00000000020}, // SPACE + {0x000000de, 0x00000000022}, // OEM_7 + {0x000000bc, 0x0000000002c}, // OEM_COMMA + {0x000000bd, 0x0000000002d}, // OEM_MINUS + {0x000000be, 0x0000000002e}, // OEM_PERIOD + {0x000000bf, 0x0000000002f}, // OEM_2 + {0x000000ba, 0x0000000003b}, // OEM_1 + {0x000000bb, 0x0000000003d}, // OEM_PLUS + {0x000000db, 0x0000000005b}, // OEM_4 + {0x000000dc, 0x0000000005c}, // OEM_5 + {0x000000dd, 0x0000000005d}, // OEM_6 + {0x000000c0, 0x00000000060}, // OEM_3 + {0x0000002e, 0x0000000007f}, // DELETE + {0x00000014, 0x00000000104}, // CAPITAL + {0x00000090, 0x0000000010a}, // NUMLOCK + {0x00000091, 0x0000000010c}, // SCROLL + {0x00000028, 0x00000000301}, // DOWN + {0x00000025, 0x00000000302}, // LEFT + {0x00000027, 0x00000000303}, // RIGHT + {0x00000026, 0x00000000304}, // UP + {0x00000023, 0x00000000305}, // END + {0x00000024, 0x00000000306}, // HOME + {0x00000022, 0x00000000307}, // NEXT + {0x00000021, 0x00000000308}, // PRIOR + {0x0000000c, 0x00000000401}, // CLEAR + {0x0000002d, 0x00000000407}, // INSERT + {0x0000001e, 0x00000000501}, // ACCEPT + {0x000000f6, 0x00000000503}, // ATTN + {0x00000003, 0x00000000504}, // CANCEL + {0x0000005d, 0x00000000505}, // APPS + {0x0000002b, 0x00000000506}, // EXECUTE + {0x0000002f, 0x00000000508}, // HELP + {0x00000013, 0x00000000509}, // PAUSE + {0x000000fa, 0x0000000050a}, // PLAY + {0x00000029, 0x0000000050c}, // SELECT + {0x0000001c, 0x00000000705}, // CONVERT + {0x0000001f, 0x0000000070b}, // MODECHANGE + {0x00000070, 0x00000000801}, // F1 + {0x00000071, 0x00000000802}, // F2 + {0x00000072, 0x00000000803}, // F3 + {0x00000073, 0x00000000804}, // F4 + {0x00000074, 0x00000000805}, // F5 + {0x00000075, 0x00000000806}, // F6 + {0x00000076, 0x00000000807}, // F7 + {0x00000077, 0x00000000808}, // F8 + {0x00000078, 0x00000000809}, // F9 + {0x00000079, 0x0000000080a}, // F10 + {0x0000007a, 0x0000000080b}, // F11 + {0x0000007b, 0x0000000080c}, // F12 + {0x0000007c, 0x0000000080d}, // F13 + {0x0000007d, 0x0000000080e}, // F14 + {0x0000007e, 0x0000000080f}, // F15 + {0x0000007f, 0x00000000810}, // F16 + {0x00000080, 0x00000000811}, // F17 + {0x00000081, 0x00000000812}, // F18 + {0x00000082, 0x00000000813}, // F19 + {0x00000083, 0x00000000814}, // F20 + {0x00000084, 0x00000000815}, // F21 + {0x00000085, 0x00000000816}, // F22 + {0x00000086, 0x00000000817}, // F23 + {0x00000087, 0x00000000818}, // F24 + {0x000000b3, 0x00000000a05}, // MEDIA_PLAY_PAUSE + {0x000000b2, 0x00000000a07}, // MEDIA_STOP + {0x0000002a, 0x00000000a0c}, // PRINT + {0x000000ae, 0x00000000a0f}, // VOLUME_DOWN + {0x000000af, 0x00000000a10}, // VOLUME_UP + {0x000000ad, 0x00000000a11}, // VOLUME_MUTE + {0x000000b4, 0x00000000b03}, // LAUNCH_MAIL + {0x000000a6, 0x00000000c01}, // BROWSER_BACK + {0x000000ab, 0x00000000c02}, // BROWSER_FAVORITES + {0x000000a7, 0x00000000c03}, // BROWSER_FORWARD + {0x000000ac, 0x00000000c04}, // BROWSER_HOME + {0x000000a8, 0x00000000c05}, // BROWSER_REFRESH + {0x000000aa, 0x00000000c06}, // BROWSER_SEARCH + {0x000000a9, 0x00000000c07}, // BROWSER_STOP + {0x000000c3, 0x0000005ff08}, // GAMEPAD_A + {0x000000c4, 0x0000005ff09}, // GAMEPAD_B + {0x000000c5, 0x0000005ff0a}, // GAMEPAD_X + {0x000000c6, 0x0000005ff0b}, // GAMEPAD_Y + {0x000000c7, 0x0000005ff0c}, // GAMEPAD_RIGHT_SHOULDER + {0x000000c8, 0x0000005ff0d}, // GAMEPAD_LEFT_SHOULDER + {0x000000c9, 0x0000005ff0e}, // GAMEPAD_LEFT_TRIGGER + {0x000000ca, 0x0000005ff0f}, // GAMEPAD_RIGHT_TRIGGER + {0x000000cb, 0x0000005ff10}, // GAMEPAD_DPAD_UP + {0x0000005f, 0x00100010082}, // SLEEP + {0x00000015, 0x00100070090}, // KANA + {0x00000015, 0x00100070090}, // HANGEUL + {0x00000015, 0x00100070090}, // HANGUL + {0x0000006a, 0x0020000002a}, // MULTIPLY + {0x0000006b, 0x0020000002b}, // ADD + {0x0000006d, 0x0020000002d}, // SUBTRACT + {0x0000006e, 0x0020000002e}, // DECIMAL + {0x0000006f, 0x0020000002f}, // DIVIDE + {0x00000060, 0x00200000030}, // NUMPAD0 + {0x00000061, 0x00200000031}, // NUMPAD1 + {0x00000062, 0x00200000032}, // NUMPAD2 + {0x00000063, 0x00200000033}, // NUMPAD3 + {0x00000064, 0x00200000034}, // NUMPAD4 + {0x00000065, 0x00200000035}, // NUMPAD5 + {0x00000066, 0x00200000036}, // NUMPAD6 + {0x00000067, 0x00200000037}, // NUMPAD7 + {0x00000068, 0x00200000038}, // NUMPAD8 + {0x00000069, 0x00200000039}, // NUMPAD9 + {0x00000092, 0x0020000003d}, // OEM_NEC_EQUAL + {0x000000a4, 0x00300000102}, // LMENU + {0x00000011, 0x00300000105}, // CONTROL + {0x000000a2, 0x00300000105}, // LCONTROL + {0x0000005b, 0x00300000109}, // LWIN + {0x00000010, 0x0030000010d}, // SHIFT + {0x000000a0, 0x0030000010d}, // LSHIFT + {0x000000a5, 0x00400000102}, // RMENU + {0x000000a3, 0x00400000105}, // RCONTROL + {0x0000005c, 0x00400000109}, // RWIN + {0x000000a1, 0x0040000010d}, // RSHIFT }; -std::map KeyboardKeyEmbedderHandler::scanCodeToLogicalMap_ = { - {0x0000e01d, 0x0400000105}, // ControlRight - {0x0000e038, 0x0400000102}, // AltRight - {0x0000004f, 0x0200000031}, // Numpad1 - {0x00000050, 0x0200000032}, // Numpad2 - {0x00000051, 0x0200000033}, // Numpad3 - {0x0000004b, 0x0200000034}, // Numpad4 - {0x0000004c, 0x0200000035}, // Numpad5 - {0x0000004d, 0x0200000036}, // Numpad6 - {0x00000047, 0x0200000037}, // Numpad7 - {0x00000048, 0x0200000038}, // Numpad8 - {0x00000049, 0x0200000039}, // Numpad9 - {0x00000052, 0x0200000030}, // Numpad0 - {0x0000004e, 0x020000002b}, // NumpadAdd - {0x00000053, 0x020000002e}, // NumpadDecimal - {0x0000e035, 0x020000002f}, // NumpadDivide - {0x00000059, 0x020000003d}, // NumpadEqual - {0x00000037, 0x020000002a}, // NumpadMultiply - {0x0000004a, 0x020000002d}, // NumpadSubtract +std::map KeyboardKeyEmbedderHandler::scanCodeToLogicalMap_ = + { + {0x0000e01d, 0x0400000105}, // ControlRight + {0x0000e038, 0x0400000102}, // AltRight + {0x0000004f, 0x0200000031}, // Numpad1 + {0x00000050, 0x0200000032}, // Numpad2 + {0x00000051, 0x0200000033}, // Numpad3 + {0x0000004b, 0x0200000034}, // Numpad4 + {0x0000004c, 0x0200000035}, // Numpad5 + {0x0000004d, 0x0200000036}, // Numpad6 + {0x00000047, 0x0200000037}, // Numpad7 + {0x00000048, 0x0200000038}, // Numpad8 + {0x00000049, 0x0200000039}, // Numpad9 + {0x00000052, 0x0200000030}, // Numpad0 + {0x0000004e, 0x020000002b}, // NumpadAdd + {0x00000053, 0x020000002e}, // NumpadDecimal + {0x0000e035, 0x020000002f}, // NumpadDivide + {0x00000059, 0x020000003d}, // NumpadEqual + {0x00000037, 0x020000002a}, // NumpadMultiply + {0x0000004a, 0x020000002d}, // NumpadSubtract }; } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 6151e294c1aed..493821a007565 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -6,9 +6,9 @@ #include -#include "flutter/shell/platform/windows/text_input_plugin.h" #include "flutter/shell/platform/windows/keyboard_key_channel_handler.h" #include "flutter/shell/platform/windows/keyboard_key_embedder_handler.h" +#include "flutter/shell/platform/windows/text_input_plugin.h" namespace flutter { @@ -79,12 +79,11 @@ void FlutterWindowsView::RegisterKeyboardHandlers( // the handler is "toggled" when events pass through, which means this logic // does not apply when there are more than 1 handler. auto key_handler = std::make_unique(SendInput); - key_handler->AddDelegate(std::make_unique( - messenger)); + key_handler->AddDelegate( + std::make_unique(messenger)); key_handler->AddDelegate(std::make_unique( - [this](const FlutterKeyEvent& event, - FlutterKeyEventCallback callback, - void* user_data) { + [this](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, + void* user_data) { return engine_->SendKeyEvent(event, callback, user_data); })); AddKeyboardHandler(std::move(key_handler)); @@ -304,8 +303,8 @@ bool FlutterWindowsView::SendKey(int key, bool extended, bool was_down) { for (const auto& handler : keyboard_handlers_) { - if (handler->KeyboardHook(this, key, scancode, action, character, - extended, was_down)) { + if (handler->KeyboardHook(this, key, scancode, action, character, extended, + was_down)) { // key event was handled, so don't send to other handlers. return true; } diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 30da60270f70b..865b0b9e09fc1 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -129,8 +129,7 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, protected: // Called to create the keyboard hook handlers. - virtual void RegisterKeyboardHandlers( - flutter::BinaryMessenger* messenger); + virtual void RegisterKeyboardHandlers(flutter::BinaryMessenger* messenger); // Used by RegisterKeyboardHandlers to add a new keyboard hook handler. void AddKeyboardHandler( @@ -269,8 +268,7 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, std::unique_ptr internal_plugin_registrar_; // Handlers for keyboard events from Windows. - std::vector> - keyboard_handlers_; + std::vector> keyboard_handlers_; // Handler for the flutter/platform channel. std::unique_ptr platform_handler_; diff --git a/shell/platform/windows/keyboard_key_channel_handler.cc b/shell/platform/windows/keyboard_key_channel_handler.cc index 15afe1132c0e8..72fbb1017fc30 100644 --- a/shell/platform/windows/keyboard_key_channel_handler.cc +++ b/shell/platform/windows/keyboard_key_channel_handler.cc @@ -100,13 +100,14 @@ KeyboardKeyChannelHandler::KeyboardKeyChannelHandler( KeyboardKeyChannelHandler::~KeyboardKeyChannelHandler() = default; -bool KeyboardKeyChannelHandler::KeyboardHook(int key, - int scancode, - int action, - char32_t character, - bool extended, - bool was_down, - std::function callback) { +bool KeyboardKeyChannelHandler::KeyboardHook( + int key, + int scancode, + int action, + char32_t character, + bool extended, + bool was_down, + std::function callback) { // TODO: Translate to a cross-platform key code system rather than passing // the native key code. rapidjson::Document event(rapidjson::kObjectType); @@ -128,8 +129,8 @@ bool KeyboardKeyChannelHandler::KeyboardHook(int key, std::cerr << "Unknown key event action: " << action << std::endl; return false; } - channel_->Send(event, [callback = std::move(callback)]( - const uint8_t* reply, size_t reply_size) { + channel_->Send(event, [callback = std::move(callback)](const uint8_t* reply, + size_t reply_size) { auto decoded = flutter::JsonMessageCodec::GetInstance().DecodeMessage( reply, reply_size); bool handled = (*decoded)[kHandledKey].GetBool(); diff --git a/shell/platform/windows/keyboard_key_channel_handler_unittests.cc b/shell/platform/windows/keyboard_key_channel_handler_unittests.cc index 8e7939b3f2e1e..388b43f8bf176 100644 --- a/shell/platform/windows/keyboard_key_channel_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_channel_handler_unittests.cc @@ -50,17 +50,17 @@ TEST(KeyboardKeyChannelHandlerTest, KeyboardHookHandling) { KeyboardKeyChannelHandler handler(&messenger); bool last_handled = false; - handler.KeyboardHook(64, kHandledScanCode, WM_KEYDOWN, L'a', - false, false, - [&last_handled](bool handled) { last_handled = handled; }); + handler.KeyboardHook( + 64, kHandledScanCode, WM_KEYDOWN, L'a', false, false, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(received_scancode, kHandledScanCode); EXPECT_EQ(last_handled, true); received_scancode = 0; - handler.KeyboardHook(64, kUnhandledScanCode, WM_KEYDOWN, L'b', - false, false, - [&last_handled](bool handled) { last_handled = handled; }); + handler.KeyboardHook( + 64, kUnhandledScanCode, WM_KEYDOWN, L'b', false, false, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(received_scancode, kUnhandledScanCode); EXPECT_EQ(last_handled, false); } @@ -90,17 +90,17 @@ TEST(KeyboardKeyChannelHandlerTest, ExtendedKeysAreSentToRedispatch) { bool last_handled = true; // Extended key flag is passed to redispatched events if set. - handler.KeyboardHook(64, kUnhandledScanCode, WM_KEYDOWN, L'b', - true, false, - [&last_handled](bool handled) { last_handled = handled; }); + handler.KeyboardHook( + 64, kUnhandledScanCode, WM_KEYDOWN, L'b', true, false, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, false); EXPECT_EQ(received_scancode, kUnhandledScanCode); last_handled = true; // Extended key flag is not passed to redispatched events if not set. - handler.KeyboardHook(64, kUnhandledScanCode, WM_KEYDOWN, L'b', - false, false, - [&last_handled](bool handled) { last_handled = handled; }); + handler.KeyboardHook( + 64, kUnhandledScanCode, WM_KEYDOWN, L'b', false, false, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, false); EXPECT_EQ(received_scancode, kUnhandledScanCode); } diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index 6d26bf3fb580e..1ac44ab59c07e 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -47,8 +47,8 @@ constexpr uint64_t kAutogeneratedMask = 0x10000000000; KeyboardKeyEmbedderHandler::KeyboardKeyEmbedderHandler( std::function send_event) + FlutterKeyEventCallback, + void* user_data)> send_event) : sendEvent_(send_event), response_id_(1) {} KeyboardKeyEmbedderHandler::~KeyboardKeyEmbedderHandler() = default; @@ -69,7 +69,8 @@ static uint16_t normalizeScancode(int windowsScanCode, bool extended) { return (windowsScanCode & 0xff) | (extended ? 0xe000 : 0); } -uint64_t KeyboardKeyEmbedderHandler::getPhysicalKey(int scancode, bool extended) { +uint64_t KeyboardKeyEmbedderHandler::getPhysicalKey(int scancode, + bool extended) { int chromiumScancode = normalizeScancode(scancode, extended); auto resultIt = windowsToPhysicalMap_.find(chromiumScancode); if (resultIt != windowsToPhysicalMap_.end()) @@ -77,11 +78,14 @@ uint64_t KeyboardKeyEmbedderHandler::getPhysicalKey(int scancode, bool extended) return scancode | kHidPlane; } -uint64_t KeyboardKeyEmbedderHandler::getLogicalKey(int key, bool extended, int scancode) { +uint64_t KeyboardKeyEmbedderHandler::getLogicalKey(int key, + bool extended, + int scancode) { // Normally logical keys should only be derived from key codes, but since some // key codes are either 0 or ambiguous (multiple keys using the same key // code), these keys are resolved by scan codes. - auto numpadIt = scanCodeToLogicalMap_.find(normalizeScancode(scancode, extended)); + auto numpadIt = + scanCodeToLogicalMap_.find(normalizeScancode(scancode, extended)); if (numpadIt != scanCodeToLogicalMap_.cend()) return numpadIt->second; @@ -216,26 +220,25 @@ bool KeyboardKeyEmbedderHandler::KeyboardHook( response_id_ += 1; uint64_t response_id = response_id_; PendingResponse pending{ - .callback = [this, callback = std::move(callback)] - (bool handled, uint64_t reponse_id) { - auto found = pending_responses_.find(reponse_id); - if (found != pending_responses_.end()) { - pending_responses_.erase(found); - } - callback(handled); - }, - .response_id = response_id, + .callback = + [this, callback = std::move(callback)](bool handled, + uint64_t reponse_id) { + auto found = pending_responses_.find(reponse_id); + if (found != pending_responses_.end()) { + pending_responses_.erase(found); + } + callback(handled); + }, + .response_id = response_id, }; auto pending_ptr = std::make_unique(std::move(pending)); pending_responses_[response_id] = std::move(pending_ptr); - sendEvent_(key_data, - KeyboardKeyEmbedderHandler::HandleResponse, + sendEvent_(key_data, KeyboardKeyEmbedderHandler::HandleResponse, reinterpret_cast(pending_responses_[response_id].get())); return true; } -void KeyboardKeyEmbedderHandler::HandleResponse( - bool handled, void* user_data) { +void KeyboardKeyEmbedderHandler::HandleResponse(bool handled, void* user_data) { PendingResponse* pending = reinterpret_cast(user_data); auto callback = std::move(pending->callback); callback(handled, pending->response_id); diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index 795b2832ab457..695c2689d422c 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -60,9 +60,8 @@ class KeyboardKeyEmbedderHandler void cacheUtf8String(char32_t ch); - std::function sendEvent_; + std::function + sendEvent_; std::map pressingRecords_; char character_cache_[kCharacterCacheSize]; std::map> pending_responses_; diff --git a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index bfdf48ab21814..7ef14c82fe13c 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -20,9 +20,7 @@ class TestFlutterKeyEvent : public FlutterKeyEvent { TestFlutterKeyEvent(const FlutterKeyEvent& src, FlutterKeyEventCallback callback, void* user_data) - : character_str(src.character), - callback(callback), - user_data(user_data) { + : character_str(src.character), callback(callback), user_data(user_data) { struct_size = src.struct_size; timestamp = src.timestamp; type = src.type; @@ -63,16 +61,15 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { std::unique_ptr handler = std::make_unique( [&results](const FlutterKeyEvent& event, - FlutterKeyEventCallback callback, - void* user_data) { + FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); }); // On a US keyboard: // Press KeyA. - handler->KeyboardHook(kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', - false, false, - [&last_handled](bool handled) { last_handled = handled; }); + handler->KeyboardHook( + kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', false, false, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, false); EXPECT_EQ(results.size(), 1); event = &results[0]; @@ -88,9 +85,9 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { results.clear(); // Hold KeyA. - handler->KeyboardHook(kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', - false, true, - [&last_handled](bool handled) { last_handled = handled; }); + handler->KeyboardHook( + kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', false, true, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, true); EXPECT_EQ(results.size(), 1); event = &results[0]; @@ -106,9 +103,9 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { results.clear(); // Release KeyA. - handler->KeyboardHook(kLogicalKeyA, kPhysicalKeyA, WM_KEYUP, 0, - false, true, - [&last_handled](bool handled) { last_handled = handled; }); + handler->KeyboardHook( + kLogicalKeyA, kPhysicalKeyA, WM_KEYUP, 0, false, true, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); @@ -127,16 +124,15 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { std::unique_ptr handler = std::make_unique( [&results](const FlutterKeyEvent& event, - FlutterKeyEventCallback callback, - void* user_data) { + FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); }); // On a US keyboard: // Press NumPad1. - handler->KeyboardHook(kLogicalNumpad1, kPhysicalNumpad1, WM_KEYDOWN, 0, - false, false, - [&last_handled](bool handled) { last_handled = handled; }); + handler->KeyboardHook( + kLogicalNumpad1, kPhysicalNumpad1, WM_KEYDOWN, 0, false, false, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); @@ -147,9 +143,9 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { results.clear(); // Press NumLock. - handler->KeyboardHook(kLogicalNumLock, kPhysicalNumLock, WM_KEYDOWN, 0, - true, false, - [&last_handled](bool handled) { last_handled = handled; }); + handler->KeyboardHook( + kLogicalNumLock, kPhysicalNumLock, WM_KEYDOWN, 0, true, false, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); @@ -160,9 +156,9 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { results.clear(); // Release NumLock. - handler->KeyboardHook(kLogicalNumLock, kPhysicalNumLock, WM_KEYUP, 0, - true, true, - [&last_handled](bool handled) { last_handled = handled; }); + handler->KeyboardHook( + kLogicalNumLock, kPhysicalNumLock, WM_KEYUP, 0, true, true, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); @@ -173,9 +169,9 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { results.clear(); // Release NumPad1. (The logical key is now NumpadEnd) - handler->KeyboardHook(kLogicalNumpadEnd, kPhysicalNumpad1, WM_KEYUP, 0, - false, true, - [&last_handled](bool handled) { last_handled = handled; }); + handler->KeyboardHook( + kLogicalNumpadEnd, kPhysicalNumpad1, WM_KEYUP, 0, false, true, + [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index 341a7776b3b3e..75e826132327d 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -54,7 +54,8 @@ uint64_t CalculateEventId(int scancode, int action, bool extended) { } // namespace -KeyboardKeyHandler::KeyboardKeyHandlerDelegate::~KeyboardKeyHandlerDelegate() = default; +KeyboardKeyHandler::KeyboardKeyHandlerDelegate::~KeyboardKeyHandlerDelegate() = + default; KeyboardKeyHandler::KeyboardKeyHandler(RedispatchEvent redispatch_event) : redispatch_event_(redispatch_event) { @@ -64,14 +65,15 @@ KeyboardKeyHandler::KeyboardKeyHandler(RedispatchEvent redispatch_event) KeyboardKeyHandler::~KeyboardKeyHandler() = default; void KeyboardKeyHandler::TextHook(FlutterWindowsView* view, - const std::u16string& code_point) {} + const std::u16string& code_point) {} void KeyboardKeyHandler::AddDelegate( - std::unique_ptr delegate) { + std::unique_ptr delegate) { delegates_.push_back(std::move(delegate)); } -const KeyboardKeyHandler::PendingEvent* KeyboardKeyHandler::FindPendingEvent(uint64_t id) { +const KeyboardKeyHandler::PendingEvent* KeyboardKeyHandler::FindPendingEvent( + uint64_t id) { if (pending_events_.empty()) { return nullptr; } @@ -98,9 +100,9 @@ void KeyboardKeyHandler::RemovePendingEvent(uint64_t id) { void KeyboardKeyHandler::DoRedispatchEvent(const PendingEvent* pending) { KEYBDINPUT ki{ - .wVk = 0, - .wScan = static_cast(pending->scancode), - .dwFlags = pending->dwFlags, + .wVk = 0, + .wScan = static_cast(pending->scancode), + .dwFlags = pending->dwFlags, }; INPUT input_event; input_event.type = INPUT_KEYBOARD; @@ -108,18 +110,19 @@ void KeyboardKeyHandler::DoRedispatchEvent(const PendingEvent* pending) { UINT accepted = redispatch_event_(1, &input_event, sizeof(input_event)); if (accepted != 1) { std::cerr << "Unable to synthesize event for unhandled keyboard event " - "with scancode " - << pending->scancode << " (character " << pending->character << ")" << std::endl; + "with scancode " + << pending->scancode << " (character " << pending->character + << ")" << std::endl; } } bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, - int key, - int scancode, - int action, - char32_t character, - bool extended, - bool was_down) { + int key, + int scancode, + int action, + char32_t character, + bool extended, + bool was_down) { const uint64_t id = CalculateEventId(scancode, action, extended); if (FindPendingEvent(id) != nullptr) { // Don't pass messages that we synthesized to the framework again. @@ -134,14 +137,14 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, << "framework. Are responses being sent?" << std::endl; } PendingEvent pending{ - .id = id, - .scancode = scancode, - .character = character, - .dwFlags = static_cast(KEYEVENTF_SCANCODE | - (extended ? KEYEVENTF_EXTENDEDKEY : 0x0) | - (action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0)), - .unreplied = delegates_.size(), - .any_handled = false, + .id = id, + .scancode = scancode, + .character = character, + .dwFlags = static_cast( + KEYEVENTF_SCANCODE | (extended ? KEYEVENTF_EXTENDEDKEY : 0x0) | + (action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0)), + .unreplied = delegates_.size(), + .any_handled = false, }; pending_events_.push_back(std::make_unique(std::move(pending))); @@ -151,8 +154,9 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, // therefore we simply assert. assert(delegates_.size() != 0); for (const auto& delegate : delegates_) { - bool isAsync = delegate->KeyboardHook(key, scancode, action, character, extended, - was_down, [pending_event = &pending_event, this](bool handled) { + bool isAsync = delegate->KeyboardHook( + key, scancode, action, character, extended, was_down, + [pending_event = &pending_event, this](bool handled) { ResolvePendingEvent(pending_event, handled); }); if (!isAsync) { @@ -162,7 +166,8 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, return true; } -void KeyboardKeyHandler::ResolvePendingEvent(PendingEvent* pending_event, bool handled) { +void KeyboardKeyHandler::ResolvePendingEvent(PendingEvent* pending_event, + bool handled) { pending_event->unreplied -= 1; pending_event->any_handled = pending_event->any_handled || handled; if (pending_event->unreplied == 0) { @@ -183,7 +188,7 @@ void KeyboardKeyHandler::ComposeEndHook() { } void KeyboardKeyHandler::ComposeChangeHook(const std::u16string& text, - int cursor_pos) { + int cursor_pos) { // Ignore. } diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index 71f393e167ce0..5596bf45802ef 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -72,11 +72,11 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { // the event is a native event, or false if the event is a redispatched one. // // Windows requires a synchronous response of whether a key event should be - // handled, while the query to Flutter is always asynchronous. This is resolved - // by "redispatching": the response to the native event is always true. If - // Flutter later decides not to handle the event, an event is then synthesized, - // dispatched to system, received again, detected, at which time |KeyboardHook| - // returns false, then falls back to other keyboard handlers. + // handled, while the query to Flutter is always asynchronous. This is + // resolved by "redispatching": the response to the native event is always + // true. If Flutter later decides not to handle the event, an event is then + // synthesized, dispatched to system, received again, detected, at which time + // |KeyboardHook| returns false, then falls back to other keyboard handlers. // // Received events are further dispatched to all added delegates. If any // delegate returns true (handled), the event is considered handled. When diff --git a/shell/platform/windows/keyboard_key_handler_unittests.cc b/shell/platform/windows/keyboard_key_handler_unittests.cc index 320dffd1c0c9b..3ac56eb2895f8 100644 --- a/shell/platform/windows/keyboard_key_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_handler_unittests.cc @@ -46,13 +46,13 @@ class MockKeyHandlerDelegate bool was_down, std::function callback) { hook_history->push_back(KeyboardHookCall{ - .delegate_id = delegate_id, - .key = key, - .scancode = scancode, - .character = character, - .extended = extended, - .was_down = was_down, - .callback = std::move(callback), + .delegate_id = delegate_id, + .key = key, + .scancode = scancode, + .character = character, + .extended = extended, + .was_down = was_down, + .callback = std::move(callback), }); return true; } @@ -69,12 +69,13 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithSingleDelegate) { // Capture the scancode of the last redispatched event int redispatch_scancode = 0; bool delegate_handled = false; - KeyboardKeyHandler handler([&redispatch_scancode](UINT cInputs, LPINPUT pInputs, - int cbSize) -> UINT { - EXPECT_TRUE(cbSize > 0); - redispatch_scancode = pInputs->ki.wScan; - return 1; - }); + KeyboardKeyHandler handler([&redispatch_scancode](UINT cInputs, + LPINPUT pInputs, + int cbSize) -> UINT { + EXPECT_TRUE(cbSize > 0); + redispatch_scancode = pInputs->ki.wScan; + return 1; + }); // Add one delegate handler.AddDelegate( std::make_unique(1, &hook_history)); @@ -82,8 +83,8 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithSingleDelegate) { /// Test 1: One event that is handled by the framework // Dispatch a key event - delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, - L'a', false, false); + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, + WM_KEYDOWN, L'a', false, false); EXPECT_EQ(delegate_handled, true); EXPECT_EQ(redispatch_scancode, 0); EXPECT_EQ(hook_history.size(), 1); @@ -101,8 +102,8 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithSingleDelegate) { // Dispatch a key event. // Also this is the same event as the previous one, to test that handled // events are cleared from the pending list. - delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, - L'a', false, false); + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, + WM_KEYDOWN, L'a', false, false); EXPECT_EQ(delegate_handled, true); EXPECT_EQ(redispatch_scancode, 0); EXPECT_EQ(hook_history.size(), 1); @@ -111,8 +112,8 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithSingleDelegate) { EXPECT_EQ(hook_history.back().was_down, false); // Dispatch another key event - delegate_handled = handler.KeyboardHook(nullptr, 65, kHandledScanCode2, WM_KEYUP, - L'b', false, true); + delegate_handled = handler.KeyboardHook(nullptr, 65, kHandledScanCode2, + WM_KEYUP, L'b', false, true); EXPECT_EQ(delegate_handled, true); EXPECT_EQ(redispatch_scancode, 0); EXPECT_EQ(hook_history.size(), 2); @@ -129,9 +130,11 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithSingleDelegate) { EXPECT_EQ(redispatch_scancode, kHandledScanCode); EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, - L'a', false, false), false); - EXPECT_EQ(handler.KeyboardHook(nullptr, 65, kHandledScanCode2, WM_KEYUP, - L'b', false, false), false); + L'a', false, false), + false); + EXPECT_EQ(handler.KeyboardHook(nullptr, 65, kHandledScanCode2, WM_KEYUP, L'b', + false, false), + false); hook_history.clear(); redispatch_scancode = 0; @@ -143,20 +146,21 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoDelegates) { // Capture the scancode of the last redispatched event int redispatch_scancode = 0; bool delegate_handled = false; - KeyboardKeyHandler handler([&redispatch_scancode](UINT cInputs, LPINPUT pInputs, - int cbSize) -> UINT { - EXPECT_TRUE(cbSize > 0); - redispatch_scancode = pInputs->ki.wScan; - return 1; - }); + KeyboardKeyHandler handler([&redispatch_scancode](UINT cInputs, + LPINPUT pInputs, + int cbSize) -> UINT { + EXPECT_TRUE(cbSize > 0); + redispatch_scancode = pInputs->ki.wScan; + return 1; + }); // Only add one delegate for now. handler.AddDelegate( std::make_unique(1, &hook_history)); /// Test 1: Add a delegate before an event is responded - delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, - L'a', false, false); + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, + WM_KEYDOWN, L'a', false, false); EXPECT_EQ(delegate_handled, true); EXPECT_EQ(redispatch_scancode, 0); EXPECT_EQ(hook_history.size(), 1); @@ -172,15 +176,16 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoDelegates) { EXPECT_EQ(redispatch_scancode, kHandledScanCode); EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, - L'a', false, false), false); + L'a', false, false), + false); redispatch_scancode = 0; hook_history.clear(); /// Test 2: A delegate responds true - delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, - L'a', false, false); + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, + WM_KEYDOWN, L'a', false, false); EXPECT_EQ(delegate_handled, true); EXPECT_EQ(redispatch_scancode, 0); EXPECT_EQ(hook_history.size(), 2); @@ -204,8 +209,8 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoDelegates) { // Also this is the same event as the previous one, to test that handled // events are cleared from the pending list. - delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, - L'a', false, false); + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, + WM_KEYDOWN, L'a', false, false); EXPECT_EQ(delegate_handled, true); EXPECT_EQ(redispatch_scancode, 0); EXPECT_EQ(hook_history.size(), 2); @@ -223,7 +228,8 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoDelegates) { EXPECT_EQ(redispatch_scancode, kHandledScanCode); EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, - L'a', false, false), false); + L'a', false, false), + false); } } // namespace testing diff --git a/shell/platform/windows/win32_flutter_window_unittests.cc b/shell/platform/windows/win32_flutter_window_unittests.cc index 11db83c7a59d9..c2e1ffc5254e5 100644 --- a/shell/platform/windows/win32_flutter_window_unittests.cc +++ b/shell/platform/windows/win32_flutter_window_unittests.cc @@ -6,13 +6,13 @@ #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" #include "flutter/shell/platform/windows/flutter_windows_engine.h" -#include "flutter/shell/platform/windows/keyboard_key_handler.h" #include "flutter/shell/platform/windows/keyboard_key_channel_handler.h" +#include "flutter/shell/platform/windows/keyboard_key_handler.h" #include "flutter/shell/platform/windows/testing/engine_embedder_api_modifier.h" #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h" #include "flutter/shell/platform/windows/testing/win32_flutter_window_test.h" -#include "flutter/shell/platform/windows/text_input_plugin_delegate.h" #include "flutter/shell/platform/windows/text_input_plugin.h" +#include "flutter/shell/platform/windows/text_input_plugin_delegate.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -52,14 +52,13 @@ struct SimulatedEvent { class SpyKeyboardKeyHandler : public KeyboardHandlerBase { public: SpyKeyboardKeyHandler(flutter::BinaryMessenger* messenger, - KeyboardKeyHandler::RedispatchEvent delegate) { - real_implementation_ = - std::make_unique(delegate); + KeyboardKeyHandler::RedispatchEvent delegate) { + real_implementation_ = std::make_unique(delegate); real_implementation_->AddDelegate( std::make_unique(messenger)); ON_CALL(*this, KeyboardHook(_, _, _, _, _, _, _)) - .WillByDefault( - Invoke(real_implementation_.get(), &KeyboardKeyHandler::KeyboardHook)); + .WillByDefault(Invoke(real_implementation_.get(), + &KeyboardKeyHandler::KeyboardHook)); ON_CALL(*this, TextHook(_, _)) .WillByDefault( Invoke(real_implementation_.get(), &KeyboardKeyHandler::TextHook)); @@ -173,8 +172,7 @@ class TestFlutterWindowsView : public FlutterWindowsView { } protected: - void RegisterKeyboardHandlers( - flutter::BinaryMessenger* messenger) override { + void RegisterKeyboardHandlers(flutter::BinaryMessenger* messenger) override { auto spy_key_event_handler = std::make_unique( messenger, [this](UINT cInputs, LPINPUT pInputs, int cbSize) -> UINT { return this->SendInput(cInputs, pInputs, cbSize); @@ -362,9 +360,9 @@ TEST(Win32FlutterWindowTest, CharKeyDownPropagation) { { test_response = false; flutter_windows_view.SetEngine(std::move(GetTestEngine())); - EXPECT_CALL( - *flutter_windows_view.key_event_handler, - KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, false, false)) + EXPECT_CALL(*flutter_windows_view.key_event_handler, + KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, + false, false)) .Times(2) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.text_input_plugin, From 063878cc97a3c5a3fec4cd2fffc180e8aac9b242 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 8 Feb 2021 23:49:44 -0800 Subject: [PATCH 50/82] Fix build --- shell/platform/windows/BUILD.gn | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 7c6c68dec5313..abe7462f4762d 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -182,6 +182,9 @@ executable("flutter_windows_unittests") { sources = [ # "flutter_project_bundle_unittests.cc", //TODO failing due to switches test failing. Blocked on https://github.com/flutter/flutter/issues/74153 # "flutter_windows_engine_unittests.cc", //TODO failing to send / receive platform message get plugins working first. Blocked on https://github.com/flutter/flutter/issues/74155 + "string_conversion_unittests.cc", + "system_utils_unittests.cc", + "testing/engine_embedder_api_modifier.h", ] # Target-specific sources. @@ -197,9 +200,6 @@ executable("flutter_windows_unittests") { "keyboard_key_channel_handler_unittests.cc", "keyboard_key_embedder_handler_unittests.cc", "keyboard_key_handler_unittests.cc", - "string_conversion_unittests.cc", - "system_utils_unittests.cc", - "testing/engine_embedder_api_modifier.h", "testing/mock_win32_window.cc", "testing/mock_win32_window.h", "testing/mock_window_binding_handler.cc", From 964df62f65c9926a46f78a42478231866c77db14 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 9 Feb 2021 01:11:55 -0800 Subject: [PATCH 51/82] Fix license --- ci/licenses_golden/licenses_flutter | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 2bffbfe051a2d..23a628b1ac324 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1470,9 +1470,6 @@ FILE: ../../../flutter/shell/platform/windows/cursor_handler.h FILE: ../../../flutter/shell/platform/windows/external_texture_gl.cc FILE: ../../../flutter/shell/platform/windows/external_texture_gl.h FILE: ../../../flutter/shell/platform/windows/flutter_key_map.cc -FILE: ../../../flutter/shell/platform/windows/flutter_keyboard_manager.cc -FILE: ../../../flutter/shell/platform/windows/flutter_keyboard_manager.h -FILE: ../../../flutter/shell/platform/windows/flutter_keyboard_manager_unittests.cc FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle.cc FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle.h FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle_unittests.cc @@ -1489,10 +1486,16 @@ FILE: ../../../flutter/shell/platform/windows/flutter_windows_view.cc FILE: ../../../flutter/shell/platform/windows/flutter_windows_view.h FILE: ../../../flutter/shell/platform/windows/flutter_windows_win32.cc FILE: ../../../flutter/shell/platform/windows/flutter_windows_winuwp.cc -FILE: ../../../flutter/shell/platform/windows/key_event_handler.cc -FILE: ../../../flutter/shell/platform/windows/key_event_handler.h -FILE: ../../../flutter/shell/platform/windows/key_event_handler_unittests.cc -FILE: ../../../flutter/shell/platform/windows/keyboard_hook_handler.h +FILE: ../../../flutter/shell/platform/windows/keyboard_handler_base.h +FILE: ../../../flutter/shell/platform/windows/keyboard_key_channel_handler.cc +FILE: ../../../flutter/shell/platform/windows/keyboard_key_channel_handler.h +FILE: ../../../flutter/shell/platform/windows/keyboard_key_channel_handler_unittests.cc +FILE: ../../../flutter/shell/platform/windows/keyboard_key_embedder_handler.cc +FILE: ../../../flutter/shell/platform/windows/keyboard_key_embedder_handler.h +FILE: ../../../flutter/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +FILE: ../../../flutter/shell/platform/windows/keyboard_key_handler.cc +FILE: ../../../flutter/shell/platform/windows/keyboard_key_handler.h +FILE: ../../../flutter/shell/platform/windows/keyboard_key_handler_unittests.cc FILE: ../../../flutter/shell/platform/windows/platform_handler.cc FILE: ../../../flutter/shell/platform/windows/platform_handler.h FILE: ../../../flutter/shell/platform/windows/platform_handler_unittests.cc From 24c603f6d6508ffb595aad7ac6392778939b4e53 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 9 Feb 2021 15:58:37 -0800 Subject: [PATCH 52/82] Fix compile --- shell/platform/windows/flutter_window_winuwp.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/windows/flutter_window_winuwp.cc b/shell/platform/windows/flutter_window_winuwp.cc index 8593b8580cd72..5b89d6029d704 100644 --- a/shell/platform/windows/flutter_window_winuwp.cc +++ b/shell/platform/windows/flutter_window_winuwp.cc @@ -268,7 +268,7 @@ void FlutterWindowWinUWP::OnKeyUp( int key = static_cast(args.VirtualKey()); char32_t chararacter = static_cast(key | 32); int action = 0x0101; - binding_handler_delegate_->OnKey(key, scancode, action, chararacter, false); + binding_handler_delegate_->OnKey(key, scancode, action, chararacter, false, true); } void FlutterWindowWinUWP::OnKeyDown( @@ -283,7 +283,7 @@ void FlutterWindowWinUWP::OnKeyDown( int key = static_cast(args.VirtualKey()); char32_t chararacter = static_cast(key | 32); int action = 0x0100; - binding_handler_delegate_->OnKey(key, scancode, action, chararacter, false); + binding_handler_delegate_->OnKey(key, scancode, action, chararacter, false, false); } void FlutterWindowWinUWP::OnCharacterReceived( From 4b534f87936b7d40041a6a1f0ed5ebb7e63a0179 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 9 Feb 2021 16:07:05 -0800 Subject: [PATCH 53/82] Fix compile. Remove unused var. --- shell/platform/windows/text_input_plugin_unittest.cc | 2 +- shell/platform/windows/win32_window.cc | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/shell/platform/windows/text_input_plugin_unittest.cc b/shell/platform/windows/text_input_plugin_unittest.cc index fe3c356b8fef0..aac581e50b4f3 100644 --- a/shell/platform/windows/text_input_plugin_unittest.cc +++ b/shell/platform/windows/text_input_plugin_unittest.cc @@ -49,7 +49,7 @@ TEST(TextInputPluginTest, TextMethodsWorksWithEmptyModel) { int redispatch_scancode = 0; TextInputPlugin handler(&messenger, &delegate); - handler.KeyboardHook(nullptr, VK_RETURN, 100, WM_KEYDOWN, '\n', false); + handler.KeyboardHook(nullptr, VK_RETURN, 100, WM_KEYDOWN, '\n', false, false); handler.ComposeBeginHook(); std::u16string text; text.push_back('\n'); diff --git a/shell/platform/windows/win32_window.cc b/shell/platform/windows/win32_window.cc index 4dd51f41a3741..d55f097c04ca2 100644 --- a/shell/platform/windows/win32_window.cc +++ b/shell/platform/windows/win32_window.cc @@ -10,11 +10,6 @@ #include "win32_dpi_utils.h" -// KeyCode used to indicate key events to be handled by the IME. These include -// the kana key, fullwidth/halfwidth (zenkaku/hankaku) key, and keypresses when -// the IME is in composing mode. -static constexpr int kImeComposingKeyCode = 229; - namespace flutter { namespace { From 65aefdb91e206ecdd2cd26228e7bf57b033b04a2 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 9 Feb 2021 16:38:31 -0800 Subject: [PATCH 54/82] Fix format --- shell/platform/windows/flutter_window_winuwp.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/shell/platform/windows/flutter_window_winuwp.cc b/shell/platform/windows/flutter_window_winuwp.cc index 5b89d6029d704..82b9dbd6bbefe 100644 --- a/shell/platform/windows/flutter_window_winuwp.cc +++ b/shell/platform/windows/flutter_window_winuwp.cc @@ -268,7 +268,8 @@ void FlutterWindowWinUWP::OnKeyUp( int key = static_cast(args.VirtualKey()); char32_t chararacter = static_cast(key | 32); int action = 0x0101; - binding_handler_delegate_->OnKey(key, scancode, action, chararacter, false, true); + binding_handler_delegate_->OnKey(key, scancode, action, chararacter, false, + true); } void FlutterWindowWinUWP::OnKeyDown( @@ -283,7 +284,8 @@ void FlutterWindowWinUWP::OnKeyDown( int key = static_cast(args.VirtualKey()); char32_t chararacter = static_cast(key | 32); int action = 0x0100; - binding_handler_delegate_->OnKey(key, scancode, action, chararacter, false, false); + binding_handler_delegate_->OnKey(key, scancode, action, chararacter, false, + false); } void FlutterWindowWinUWP::OnCharacterReceived( From db2309c77784f6ca8fe94a8ecac6d51fb0f5c1df Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 10 Feb 2021 02:09:13 -0800 Subject: [PATCH 55/82] Add handle tests --- .../platform/windows/keyboard_key_handler.cc | 18 ++- shell/platform/windows/keyboard_key_handler.h | 8 +- .../windows/keyboard_key_handler_unittests.cc | 124 +++++++++++++++++- 3 files changed, 136 insertions(+), 14 deletions(-) diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index 75e826132327d..2e963d2b65ec0 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -153,25 +153,29 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, // never happen in real life since all delegates are added by hardcode, // therefore we simply assert. assert(delegates_.size() != 0); + bool any_async = false; for (const auto& delegate : delegates_) { - bool isAsync = delegate->KeyboardHook( + bool is_async = delegate->KeyboardHook( key, scancode, action, character, extended, was_down, [pending_event = &pending_event, this](bool handled) { - ResolvePendingEvent(pending_event, handled); + ResolvePendingEvent(pending_event, handled, true); }); - if (!isAsync) { - ResolvePendingEvent(&pending_event, false); + if (!is_async) { + ResolvePendingEvent(&pending_event, false, false); + } else { + any_async = true; } } - return true; + return any_async; } void KeyboardKeyHandler::ResolvePendingEvent(PendingEvent* pending_event, - bool handled) { + bool handled, + bool may_redispatch) { pending_event->unreplied -= 1; pending_event->any_handled = pending_event->any_handled || handled; if (pending_event->unreplied == 0) { - if (!pending_event->any_handled) { + if (!pending_event->any_handled && may_redispatch) { DoRedispatchEvent(pending_event); } else { RemovePendingEvent(pending_event->id); diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index 5596bf45802ef..37be4f2d92945 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -24,10 +24,10 @@ class FlutterWindowsView; // This class detects whether an incoming event is a redispatched one, // dispatches native events to delegates and collect their responses, // and redispatches events unhandled by Flutter back to the system. -// See |KeyboardHook| for more information. +// See |KeyboardHook| for more information about dispatching. // -// The exact behavior to handle events are further divided into delegates. -// See |KeyboardKeyHandlerDelegate| and its subclasses. +// The exact behavior to handle events are further forwarded into +// delegates. See |KeyboardKeyHandlerDelegate| and its subclasses. class KeyboardKeyHandler : public KeyboardHandlerBase { public: // An interface for concrete definition of how to asynchronously handle key @@ -126,7 +126,7 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { int scancode, int character); void DoRedispatchEvent(const PendingEvent* pending); - void ResolvePendingEvent(PendingEvent* pending, bool handled); + void ResolvePendingEvent(PendingEvent* pending, bool handled, bool may_redispatch); std::vector> delegates_; diff --git a/shell/platform/windows/keyboard_key_handler_unittests.cc b/shell/platform/windows/keyboard_key_handler_unittests.cc index 3ac56eb2895f8..4af035bc2118b 100644 --- a/shell/platform/windows/keyboard_key_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_handler_unittests.cc @@ -18,9 +18,14 @@ static constexpr int kHandledScanCode = 20; static constexpr int kHandledScanCode2 = 22; static constexpr int kUnhandledScanCode = 21; +// A testing |KeyHandlerDelegate| that records all calls +// to |KeyboardHook| and can be customized with whether +// the hook is handled in async. class MockKeyHandlerDelegate : public KeyboardKeyHandler::KeyboardKeyHandlerDelegate { public: + using GetIsAsync = std::function; + class KeyboardHookCall { public: int delegate_id; @@ -33,9 +38,22 @@ class MockKeyHandlerDelegate std::function callback; }; + // Create a |MockKeyHandlerDelegate|. + // + // The |delegate_id| is an arbitrary ID to tell between delegates + // that will be recorded in |KeyboardHookCall|. + // + // The |hook_history| will store every call to |KeyboardHookCall| that are + // handled asynchronously. + // + // The |is_async| is a function that the class calls upon every + // |KeyboardHookCall| to decide whether the call is handled asynchronously. + // Defaults to always returning true (async). MockKeyHandlerDelegate(int delegate_id, - std::list* hook_history) - : delegate_id(delegate_id), hook_history(hook_history) {} + std::list* hook_history, + GetIsAsync is_async = [] { return true; }) + : delegate_id(delegate_id), hook_history(hook_history), + get_is_async(get_is_async) {} virtual ~MockKeyHandlerDelegate() = default; virtual bool KeyboardHook(int key, @@ -45,6 +63,9 @@ class MockKeyHandlerDelegate bool extended, bool was_down, std::function callback) { + if (!is_async()) { + return false; + } hook_history->push_back(KeyboardHookCall{ .delegate_id = delegate_id, .key = key, @@ -57,6 +78,7 @@ class MockKeyHandlerDelegate return true; } + GetIsAsync is_async; int delegate_id; std::list* hook_history; }; @@ -140,7 +162,7 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithSingleDelegate) { redispatch_scancode = 0; } -TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoDelegates) { +TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoAsyncDelegates) { std::list hook_history; // Capture the scancode of the last redispatched event @@ -232,5 +254,101 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoDelegates) { false); } +TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithAsyncAndSyncDelegates) { + std::list hook_history; + + // Capture the scancode of the last redispatched event + int redispatch_scancode = 0; + bool delegate_handled = false; + KeyboardKeyHandler handler([&redispatch_scancode](UINT cInputs, + LPINPUT pInputs, + int cbSize) -> UINT { + EXPECT_TRUE(cbSize > 0); + redispatch_scancode = pInputs->ki.wScan; + return 1; + }); + // Add one delegate + bool delegate1_is_async = true; + auto delegate1 = std::make_unique(1, &hook_history, + [&delegate1_is_async]() { return delegate1_is_async; }); + handler.AddDelegate(std::move(delegate1)); + + /// Test 1: The only delegate is sync. + + delegate1_is_async = false; + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, + WM_KEYDOWN, L'a', false, false); + EXPECT_EQ(delegate_handled, false); + EXPECT_EQ(redispatch_scancode, 0); + EXPECT_EQ(hook_history.size(), 0); + + // Add another delegate + bool delegate2_is_async = true; + auto delegate2 = std::make_unique(2, &hook_history, + [&delegate2_is_async]() { return delegate2_is_async; }); + handler.AddDelegate(std::move(delegate2)); + + redispatch_scancode = 0; + hook_history.clear(); + + /// Test 2: Both delegates are sync + + // Also this is the same event as the previous one, to test that handled + // events are cleared from the pending list. + + delegate1_is_async = false; + delegate2_is_async = false; + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, + WM_KEYDOWN, L'a', false, false); + EXPECT_EQ(delegate_handled, false); + EXPECT_EQ(redispatch_scancode, 0); + EXPECT_EQ(hook_history.size(), 0); + + redispatch_scancode = 0; + hook_history.clear(); + + /// Test 3: Only one delegate is sync, the other responds false + + // Also this is the same event as the previous one, to test that handled + // events are cleared from the pending list. + delegate1_is_async = true; + delegate2_is_async = false; + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, + WM_KEYDOWN, L'a', false, false); + EXPECT_EQ(delegate_handled, true); + EXPECT_EQ(redispatch_scancode, 0); + EXPECT_EQ(hook_history.size(), 1); + EXPECT_EQ(hook_history.front().delegate_id, 1); + EXPECT_EQ(hook_history.front().scancode, kHandledScanCode); + EXPECT_EQ(hook_history.front().was_down, false); + + hook_history.front().callback(false); + EXPECT_EQ(redispatch_scancode, kHandledScanCode); + + EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, + L'a', false, false), + false); + redispatch_scancode = 0; + hook_history.clear(); + + /// Test 4: Only one delegate is sync, the other responds true + + // Also this is the same event as the previous one, to test that handled + // events are cleared from the pending list. + delegate1_is_async = true; + delegate2_is_async = false; + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, + WM_KEYDOWN, L'a', false, false); + EXPECT_EQ(delegate_handled, true); + EXPECT_EQ(redispatch_scancode, 0); + EXPECT_EQ(hook_history.size(), 1); + EXPECT_EQ(hook_history.front().delegate_id, 1); + EXPECT_EQ(hook_history.front().scancode, kHandledScanCode); + EXPECT_EQ(hook_history.front().was_down, false); + + hook_history.front().callback(true); + EXPECT_EQ(redispatch_scancode, 0); +} + } // namespace testing } // namespace flutter From 8fa509cd740fe88c0f12cd30954b7ecb40fc5f25 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 10 Feb 2021 02:20:51 -0800 Subject: [PATCH 56/82] Simplify logic --- .../platform/windows/keyboard_key_handler.cc | 35 ++++++++----------- shell/platform/windows/keyboard_key_handler.h | 2 +- .../windows/keyboard_key_handler_unittests.cc | 2 +- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index 2e963d2b65ec0..a5bc9d9200386 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -136,7 +136,7 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, << " keyboard events that have not yet received a response from the " << "framework. Are responses being sent?" << std::endl; } - PendingEvent pending{ + auto pending_event = std::make_unique(PendingEvent{ .id = id, .scancode = scancode, .character = character, @@ -145,37 +145,32 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, (action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0)), .unreplied = delegates_.size(), .any_handled = false, - }; - pending_events_.push_back(std::make_unique(std::move(pending))); - - PendingEvent& pending_event = *pending_events_.back(); - // If delegates_ is empty, we need to respond immediately. But this would - // never happen in real life since all delegates are added by hardcode, - // therefore we simply assert. - assert(delegates_.size() != 0); - bool any_async = false; + }); + + int unreplied = 0; for (const auto& delegate : delegates_) { bool is_async = delegate->KeyboardHook( key, scancode, action, character, extended, was_down, - [pending_event = &pending_event, this](bool handled) { - ResolvePendingEvent(pending_event, handled, true); + [pending_event = pending_event.get(), this](bool handled) { + ResolvePendingEvent(pending_event, handled); }); - if (!is_async) { - ResolvePendingEvent(&pending_event, false, false); - } else { - any_async = true; + if (is_async) { + unreplied += 1; } } - return any_async; + pending_event->unreplied = unreplied; + if (unreplied > 0) { + pending_events_.push_back(std::move(pending_event)); + } + return unreplied > 0; } void KeyboardKeyHandler::ResolvePendingEvent(PendingEvent* pending_event, - bool handled, - bool may_redispatch) { + bool handled) { pending_event->unreplied -= 1; pending_event->any_handled = pending_event->any_handled || handled; if (pending_event->unreplied == 0) { - if (!pending_event->any_handled && may_redispatch) { + if (!pending_event->any_handled) { DoRedispatchEvent(pending_event); } else { RemovePendingEvent(pending_event->id); diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index 37be4f2d92945..49041a1d6e45b 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -126,7 +126,7 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { int scancode, int character); void DoRedispatchEvent(const PendingEvent* pending); - void ResolvePendingEvent(PendingEvent* pending, bool handled, bool may_redispatch); + void ResolvePendingEvent(PendingEvent* pending, bool handled); std::vector> delegates_; diff --git a/shell/platform/windows/keyboard_key_handler_unittests.cc b/shell/platform/windows/keyboard_key_handler_unittests.cc index 4af035bc2118b..14621bcc39b58 100644 --- a/shell/platform/windows/keyboard_key_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_handler_unittests.cc @@ -53,7 +53,7 @@ class MockKeyHandlerDelegate std::list* hook_history, GetIsAsync is_async = [] { return true; }) : delegate_id(delegate_id), hook_history(hook_history), - get_is_async(get_is_async) {} + is_async(is_async) {} virtual ~MockKeyHandlerDelegate() = default; virtual bool KeyboardHook(int key, From 85c944f9fb36894b3a73f1661e0265d1d8c0b298 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 10 Feb 2021 02:23:31 -0800 Subject: [PATCH 57/82] Rename to RedispatchEvent --- shell/platform/windows/keyboard_key_handler.cc | 6 +++--- shell/platform/windows/keyboard_key_handler.h | 8 ++++---- shell/platform/windows/win32_flutter_window_unittests.cc | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index a5bc9d9200386..b1ec06f0d0b40 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -57,7 +57,7 @@ uint64_t CalculateEventId(int scancode, int action, bool extended) { KeyboardKeyHandler::KeyboardKeyHandlerDelegate::~KeyboardKeyHandlerDelegate() = default; -KeyboardKeyHandler::KeyboardKeyHandler(RedispatchEvent redispatch_event) +KeyboardKeyHandler::KeyboardKeyHandler(EventRedispatcher redispatch_event) : redispatch_event_(redispatch_event) { assert(redispatch_event_ != nullptr); } @@ -98,7 +98,7 @@ void KeyboardKeyHandler::RemovePendingEvent(uint64_t id) { << ", but the event was not found." << std::endl; } -void KeyboardKeyHandler::DoRedispatchEvent(const PendingEvent* pending) { +void KeyboardKeyHandler::RedispatchEvent(const PendingEvent* pending) { KEYBDINPUT ki{ .wVk = 0, .wScan = static_cast(pending->scancode), @@ -171,7 +171,7 @@ void KeyboardKeyHandler::ResolvePendingEvent(PendingEvent* pending_event, pending_event->any_handled = pending_event->any_handled || handled; if (pending_event->unreplied == 0) { if (!pending_event->any_handled) { - DoRedispatchEvent(pending_event); + RedispatchEvent(pending_event); } else { RemovePendingEvent(pending_event->id); } diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index 49041a1d6e45b..0de36157f5fdc 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -53,13 +53,13 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { virtual ~KeyboardKeyHandlerDelegate(); }; - using RedispatchEvent = + using EventRedispatcher = std::function; // Create a KeyboardKeyHandler and specify where to redispatch events. // // The |redispatch_event| is typically |SendInput|. - explicit KeyboardKeyHandler(RedispatchEvent redispatch_event); + explicit KeyboardKeyHandler(EventRedispatcher redispatch_event); ~KeyboardKeyHandler(); @@ -125,7 +125,7 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { bool extended, int scancode, int character); - void DoRedispatchEvent(const PendingEvent* pending); + void RedispatchEvent(const PendingEvent* pending); void ResolvePendingEvent(PendingEvent* pending, bool handled); std::vector> delegates_; @@ -135,7 +135,7 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { std::deque> pending_events_; // A function used to redispatch synthesized events. - RedispatchEvent redispatch_event_; + EventRedispatcher redispatch_event_; }; } // namespace flutter diff --git a/shell/platform/windows/win32_flutter_window_unittests.cc b/shell/platform/windows/win32_flutter_window_unittests.cc index c2e1ffc5254e5..f4f9df387a60f 100644 --- a/shell/platform/windows/win32_flutter_window_unittests.cc +++ b/shell/platform/windows/win32_flutter_window_unittests.cc @@ -52,7 +52,7 @@ struct SimulatedEvent { class SpyKeyboardKeyHandler : public KeyboardHandlerBase { public: SpyKeyboardKeyHandler(flutter::BinaryMessenger* messenger, - KeyboardKeyHandler::RedispatchEvent delegate) { + KeyboardKeyHandler::EventRedispatcher delegate) { real_implementation_ = std::make_unique(delegate); real_implementation_->AddDelegate( std::make_unique(messenger)); From 8791ca8667ed5ebad31e46a7398aedad0ca8f2ba Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 10 Feb 2021 02:26:06 -0800 Subject: [PATCH 58/82] Remove redundant APIs --- shell/platform/windows/keyboard_key_handler.cc | 6 +----- shell/platform/windows/keyboard_key_handler.h | 3 +-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index b1ec06f0d0b40..ceef1728371ba 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -67,11 +67,6 @@ KeyboardKeyHandler::~KeyboardKeyHandler() = default; void KeyboardKeyHandler::TextHook(FlutterWindowsView* view, const std::u16string& code_point) {} -void KeyboardKeyHandler::AddDelegate( - std::unique_ptr delegate) { - delegates_.push_back(std::move(delegate)); -} - const KeyboardKeyHandler::PendingEvent* KeyboardKeyHandler::FindPendingEvent( uint64_t id) { if (pending_events_.empty()) { @@ -168,6 +163,7 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, void KeyboardKeyHandler::ResolvePendingEvent(PendingEvent* pending_event, bool handled) { pending_event->unreplied -= 1; + assert(pending_event->unreplied >= 0); pending_event->any_handled = pending_event->any_handled || handled; if (pending_event->unreplied == 0) { if (!pending_event->any_handled) { diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index 0de36157f5fdc..dc0f3f7184908 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -117,8 +117,6 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { }; const PendingEvent* FindPendingEvent(uint64_t id); - void RemovePendingEvent(uint64_t id); - void AddPendingEvent(uint64_t id, int scancode, int action, bool extended); void HandleResponse(bool handled, uint64_t id, int action, @@ -126,6 +124,7 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { int scancode, int character); void RedispatchEvent(const PendingEvent* pending); + void RemovePendingEvent(uint64_t id); void ResolvePendingEvent(PendingEvent* pending, bool handled); std::vector> delegates_; From c5b179de890219f62602ea417bfa28b9b2aa5099 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 10 Feb 2021 14:28:52 -0800 Subject: [PATCH 59/82] Fix compile --- .../keyboard_key_embedder_handler_unittests.cc | 12 ++++++++++-- shell/platform/windows/keyboard_key_handler.cc | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index 7ef14c82fe13c..6d7e34c572cf2 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -53,6 +53,8 @@ constexpr uint64_t kLogicalNumLock = 0x90; } // namespace // Test the most basic key events. +// +// Press, hold, and release key A on an US keyboard. TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { std::vector results; TestFlutterKeyEvent* event; @@ -65,7 +67,6 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { results.emplace_back(event, callback, user_data); }); - // On a US keyboard: // Press KeyA. handler->KeyboardHook( kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', false, false, @@ -116,6 +117,11 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { event->callback(false, event->user_data); } +// Press numpad 1, toggle NumLock, and release numpad 1 on an US +// keyboard. +// +// This is special because the virtual key for numpad 1 will +// change in this process. TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { std::vector results; TestFlutterKeyEvent* event; @@ -128,7 +134,6 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { results.emplace_back(event, callback, user_data); }); - // On a US keyboard: // Press NumPad1. handler->KeyboardHook( kLogicalNumpad1, kPhysicalNumpad1, WM_KEYDOWN, 0, false, false, @@ -182,5 +187,8 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { results.clear(); } +TEST(KeyboardKeyEmbedderHandlerTest, ImeEventsAreIgnored) { +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index ceef1728371ba..5df91375abd16 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -67,6 +67,11 @@ KeyboardKeyHandler::~KeyboardKeyHandler() = default; void KeyboardKeyHandler::TextHook(FlutterWindowsView* view, const std::u16string& code_point) {} +void KeyboardKeyHandler::AddDelegate( + std::unique_ptr delegate) { + delegates_.push_back(std::move(delegate)); +} + const KeyboardKeyHandler::PendingEvent* KeyboardKeyHandler::FindPendingEvent( uint64_t id) { if (pending_events_.empty()) { From 34a54e318dcea673eb0e06eceba25c9c270bfa4d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 11 Feb 2021 16:24:19 -0800 Subject: [PATCH 60/82] New struct of handler --- .../windows/keyboard_key_channel_handler.cc | 6 +- .../windows/keyboard_key_channel_handler.h | 2 +- .../windows/keyboard_key_embedder_handler.cc | 18 +- .../windows/keyboard_key_embedder_handler.h | 4 +- .../platform/windows/keyboard_key_handler.cc | 30 +-- shell/platform/windows/keyboard_key_handler.h | 17 +- .../windows/keyboard_key_handler_unittests.cc | 221 +++++++++--------- .../windows/testing/mock_win32_window.h | 1 + .../windows/win32_flutter_window_unittests.cc | 30 ++- shell/platform/windows/win32_window.cc | 7 +- shell/platform/windows/win32_window.h | 3 + .../windows/win32_window_unittests.cc | 6 +- 12 files changed, 181 insertions(+), 164 deletions(-) diff --git a/shell/platform/windows/keyboard_key_channel_handler.cc b/shell/platform/windows/keyboard_key_channel_handler.cc index 72fbb1017fc30..a5b0790b98ad4 100644 --- a/shell/platform/windows/keyboard_key_channel_handler.cc +++ b/shell/platform/windows/keyboard_key_channel_handler.cc @@ -100,7 +100,7 @@ KeyboardKeyChannelHandler::KeyboardKeyChannelHandler( KeyboardKeyChannelHandler::~KeyboardKeyChannelHandler() = default; -bool KeyboardKeyChannelHandler::KeyboardHook( +void KeyboardKeyChannelHandler::KeyboardHook( int key, int scancode, int action, @@ -127,7 +127,8 @@ bool KeyboardKeyChannelHandler::KeyboardHook( break; default: std::cerr << "Unknown key event action: " << action << std::endl; - return false; + callback(false); + return; } channel_->Send(event, [callback = std::move(callback)](const uint8_t* reply, size_t reply_size) { @@ -136,7 +137,6 @@ bool KeyboardKeyChannelHandler::KeyboardHook( bool handled = (*decoded)[kHandledKey].GetBool(); callback(handled); }); - return true; } } // namespace flutter diff --git a/shell/platform/windows/keyboard_key_channel_handler.h b/shell/platform/windows/keyboard_key_channel_handler.h index f2c102d70638f..ff9aa1a8cf4b6 100644 --- a/shell/platform/windows/keyboard_key_channel_handler.h +++ b/shell/platform/windows/keyboard_key_channel_handler.h @@ -30,7 +30,7 @@ class KeyboardKeyChannelHandler ~KeyboardKeyChannelHandler(); // |KeyboardKeyHandler::KeyboardKeyHandlerDelegate| - bool KeyboardHook(int key, + void KeyboardHook(int key, int scancode, int action, char32_t character, diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index 1ac44ab59c07e..4fa2128044f03 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -115,7 +115,7 @@ uint64_t KeyboardKeyEmbedderHandler::getLogicalKey(int key, } } -void KeyboardKeyEmbedderHandler::cacheUtf8String(char32_t character) { +void KeyboardKeyEmbedderHandler::CacheUtf8String(char32_t character) { if (character == 0) { character_cache_[0] = '\0'; return; @@ -125,7 +125,7 @@ void KeyboardKeyEmbedderHandler::cacheUtf8String(char32_t character) { strcpy_s(character_cache_, kCharacterCacheSize, Utf8FromUtf16(text).c_str()); } -bool KeyboardKeyEmbedderHandler::KeyboardHook( +void KeyboardKeyEmbedderHandler::KeyboardHook( int key, int scancode, int action, @@ -157,7 +157,7 @@ bool KeyboardKeyEmbedderHandler::KeyboardHook( // A normal repeated key. type = kFlutterKeyEventTypeRepeat; assert(had_record); - cacheUtf8String(character); + CacheUtf8String(character); next_logical_record = last_logical_record; result_logical_key = last_logical_record; } else { @@ -165,13 +165,14 @@ bool KeyboardKeyEmbedderHandler::KeyboardHook( // as a currently pressed one, usually indicating multiple keyboards are // pressing keys with the same physical key, or the up event was lost // during a loss of focus. The down event is ignored. - return false; + callback(true); + return; } } else { // A normal down event (whether the system event is a repeat or not). type = kFlutterKeyEventTypeDown; assert(!had_record); - cacheUtf8String(character); + CacheUtf8String(character); next_logical_record = logical_key; result_logical_key = logical_key; } @@ -179,7 +180,8 @@ bool KeyboardKeyEmbedderHandler::KeyboardHook( if (last_logical_record == 0) { // The physical key has been released before. It indicates multiple // keyboards pressed keys with the same physical key. Ignore the up event. - return false; + callback(true); + return; } else { // A normal up event. type = kFlutterKeyEventTypeUp; @@ -202,7 +204,8 @@ bool KeyboardKeyEmbedderHandler::KeyboardHook( // presses are considered handled and not sent to Flutter. These events must // be filtered by result_logical_key because the key up event of such // presses uses the "original" logical key. - return false; + callback(true); + return; } FlutterKeyEvent key_data = {}; @@ -235,7 +238,6 @@ bool KeyboardKeyEmbedderHandler::KeyboardHook( pending_responses_[response_id] = std::move(pending_ptr); sendEvent_(key_data, KeyboardKeyEmbedderHandler::HandleResponse, reinterpret_cast(pending_responses_[response_id].get())); - return true; } void KeyboardKeyEmbedderHandler::HandleResponse(bool handled, void* user_data) { diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index 695c2689d422c..8f6020c2384aa 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -44,7 +44,7 @@ class KeyboardKeyEmbedderHandler virtual ~KeyboardKeyEmbedderHandler(); // |KeyboardHandlerBase| - bool KeyboardHook(int key, + void KeyboardHook(int key, int scancode, int action, char32_t character, @@ -58,7 +58,7 @@ class KeyboardKeyEmbedderHandler uint64_t response_id; }; - void cacheUtf8String(char32_t ch); + void CacheUtf8String(char32_t ch); std::function sendEvent_; diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index 5df91375abd16..a6be51cbf8a34 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -72,6 +72,10 @@ void KeyboardKeyHandler::AddDelegate( delegates_.push_back(std::move(delegate)); } +size_t KeyboardKeyHandler::PendingAmount() { + return pending_events_.size(); +} + const KeyboardKeyHandler::PendingEvent* KeyboardKeyHandler::FindPendingEvent( uint64_t id) { if (pending_events_.empty()) { @@ -136,7 +140,7 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, << " keyboard events that have not yet received a response from the " << "framework. Are responses being sent?" << std::endl; } - auto pending_event = std::make_unique(PendingEvent{ + pending_events_.push_back(std::make_unique(PendingEvent{ .id = id, .scancode = scancode, .character = character, @@ -145,24 +149,24 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, (action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0)), .unreplied = delegates_.size(), .any_handled = false, - }); + })); + PendingEvent& pending_event = *pending_events_.back(); - int unreplied = 0; for (const auto& delegate : delegates_) { - bool is_async = delegate->KeyboardHook( + delegate->KeyboardHook( key, scancode, action, character, extended, was_down, - [pending_event = pending_event.get(), this](bool handled) { + [pending_event = &pending_event, this](bool handled) { ResolvePendingEvent(pending_event, handled); }); - if (is_async) { - unreplied += 1; - } } - pending_event->unreplied = unreplied; - if (unreplied > 0) { - pending_events_.push_back(std::move(pending_event)); - } - return unreplied > 0; + + // |ResolvePendingEvent| might trigger redispatching synchronously, + // which might occur before |KeyboardHook| is returned. This won't + // make events out of order though, because |KeyboardHook| will always + // return true at this time, preventing this event from affecting + // others. + + return true; } void KeyboardKeyHandler::ResolvePendingEvent(PendingEvent* pending_event, diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index dc0f3f7184908..e857a941ee73b 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -36,13 +36,9 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { public: // Defines how to how to asynchronously handle key events. // - // If the delegate will wait for an asynchronous response for this event, - // |KeyboardHook| should return true, and invoke |callback| with the - // response (whether the event is handled) later for exactly once. - // - // Otherwise, the delegate should return false and *never* invoke - // |callback|. This is considered not handling the event. - virtual bool KeyboardHook(int key, + // |KeyboardHook| should invoke |callback| with the response (whether the + // event is handled) later for exactly once. + virtual void KeyboardHook(int key, int scancode, int action, char32_t character, @@ -106,13 +102,20 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { // |KeyboardHandlerBase| void ComposeChangeHook(const std::u16string& text, int cursor_pos) override; + protected: + size_t PendingAmount(); + private: struct PendingEvent { uint64_t id; int scancode; char32_t character; DWORD dwFlags; + DWORD lParam; + + // The number of delegates that haven't replied. size_t unreplied; + // Whether any replied delegates reported true (handled). bool any_handled; }; diff --git a/shell/platform/windows/keyboard_key_handler_unittests.cc b/shell/platform/windows/keyboard_key_handler_unittests.cc index 14621bcc39b58..ce2beb0963fe8 100644 --- a/shell/platform/windows/keyboard_key_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_handler_unittests.cc @@ -18,13 +18,18 @@ static constexpr int kHandledScanCode = 20; static constexpr int kHandledScanCode2 = 22; static constexpr int kUnhandledScanCode = 21; +typedef std::function Callback; +typedef std::function CallbackHandler; +void dont_respond(Callback& callback) {} +void respond_true(Callback& callback) { callback(true); } +void respond_false(Callback& callback) { callback(false); } + // A testing |KeyHandlerDelegate| that records all calls // to |KeyboardHook| and can be customized with whether // the hook is handled in async. class MockKeyHandlerDelegate : public KeyboardKeyHandler::KeyboardKeyHandlerDelegate { public: - using GetIsAsync = std::function; class KeyboardHookCall { public: @@ -50,22 +55,18 @@ class MockKeyHandlerDelegate // |KeyboardHookCall| to decide whether the call is handled asynchronously. // Defaults to always returning true (async). MockKeyHandlerDelegate(int delegate_id, - std::list* hook_history, - GetIsAsync is_async = [] { return true; }) + std::list* hook_history) : delegate_id(delegate_id), hook_history(hook_history), - is_async(is_async) {} + callback_handler(dont_respond) {} virtual ~MockKeyHandlerDelegate() = default; - virtual bool KeyboardHook(int key, + virtual void KeyboardHook(int key, int scancode, int action, char32_t character, bool extended, bool was_down, std::function callback) { - if (!is_async()) { - return false; - } hook_history->push_back(KeyboardHookCall{ .delegate_id = delegate_id, .key = key, @@ -75,55 +76,64 @@ class MockKeyHandlerDelegate .was_down = was_down, .callback = std::move(callback), }); - return true; + callback_handler(hook_history->back().callback); } - GetIsAsync is_async; + CallbackHandler callback_handler; int delegate_id; std::list* hook_history; }; +class TestKeyboardKeyHandler : public KeyboardKeyHandler { + public: + explicit TestKeyboardKeyHandler(EventRedispatcher redispatch_event) + : KeyboardKeyHandler(redispatch_event) {} + + bool HasPending() { + return PendingAmount() > 0; + } +}; + } // namespace -TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithSingleDelegate) { +TEST(KeyboardKeyHandlerTest, SingleDelegateWithAsyncResponds) { std::list hook_history; // Capture the scancode of the last redispatched event int redispatch_scancode = 0; bool delegate_handled = false; - KeyboardKeyHandler handler([&redispatch_scancode](UINT cInputs, - LPINPUT pInputs, - int cbSize) -> UINT { + TestKeyboardKeyHandler handler([&redispatch_scancode](UINT cInputs, + LPINPUT pInputs, + int cbSize) -> UINT { EXPECT_TRUE(cbSize > 0); redispatch_scancode = pInputs->ki.wScan; return 1; }); // Add one delegate - handler.AddDelegate( - std::make_unique(1, &hook_history)); + auto delegate = std::make_unique(1, &hook_history); + handler.AddDelegate(std::move(delegate)); /// Test 1: One event that is handled by the framework // Dispatch a key event delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, - WM_KEYDOWN, L'a', false, false); + WM_KEYDOWN, L'a', false, true); EXPECT_EQ(delegate_handled, true); EXPECT_EQ(redispatch_scancode, 0); EXPECT_EQ(hook_history.size(), 1); EXPECT_EQ(hook_history.back().delegate_id, 1); EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); - EXPECT_EQ(hook_history.back().was_down, false); + EXPECT_EQ(hook_history.back().was_down, true); + EXPECT_EQ(handler.HasPending(), true); hook_history.back().callback(true); EXPECT_EQ(redispatch_scancode, 0); + EXPECT_EQ(handler.HasPending(), false); hook_history.clear(); /// Test 2: Two events that are unhandled by the framework - // Dispatch a key event. - // Also this is the same event as the previous one, to test that handled - // events are cleared from the pending list. delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, L'a', false, false); EXPECT_EQ(delegate_handled, true); @@ -158,29 +168,33 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithSingleDelegate) { false, false), false); + EXPECT_EQ(handler.HasPending(), false); hook_history.clear(); redispatch_scancode = 0; } -TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoAsyncDelegates) { +TEST(KeyboardKeyHandlerTest, SingleDelegateWithSyncResponds) { std::list hook_history; // Capture the scancode of the last redispatched event int redispatch_scancode = 0; bool delegate_handled = false; - KeyboardKeyHandler handler([&redispatch_scancode](UINT cInputs, - LPINPUT pInputs, - int cbSize) -> UINT { + TestKeyboardKeyHandler handler([&redispatch_scancode](UINT cInputs, + LPINPUT pInputs, + int cbSize) -> UINT { EXPECT_TRUE(cbSize > 0); redispatch_scancode = pInputs->ki.wScan; return 1; }); - // Only add one delegate for now. - handler.AddDelegate( - std::make_unique(1, &hook_history)); + // Add one delegate + auto delegate = std::make_unique(1, &hook_history); + CallbackHandler& delegate_handler = delegate->callback_handler; + handler.AddDelegate(std::move(delegate)); - /// Test 1: Add a delegate before an event is responded + /// Test 1: One event that is handled by the framework + // Dispatch a key event + delegate_handler = respond_true; delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, L'a', false, false); EXPECT_EQ(delegate_handled, true); @@ -190,21 +204,56 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoAsyncDelegates) { EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); EXPECT_EQ(hook_history.back().was_down, false); - handler.AddDelegate( - std::make_unique(2, &hook_history)); + EXPECT_EQ(handler.HasPending(), false); + hook_history.clear(); - // Only one reply is needed because the 2nd delegate is added late. - hook_history.back().callback(false); + /// Test 2: An event unhandled by the framework + + delegate_handler = respond_false; + delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, + WM_KEYDOWN, L'a', false, false); + EXPECT_EQ(delegate_handled, true); EXPECT_EQ(redispatch_scancode, kHandledScanCode); + EXPECT_EQ(hook_history.size(), 1); + EXPECT_EQ(hook_history.back().delegate_id, 1); + EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); + EXPECT_EQ(hook_history.back().was_down, false); + + EXPECT_EQ(handler.HasPending(), true); + // Resolve the event EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, L'a', false, false), false); - redispatch_scancode = 0; + EXPECT_EQ(handler.HasPending(), false); hook_history.clear(); + redispatch_scancode = 0; +} + +TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoAsyncDelegates) { + std::list hook_history; + + // Capture the scancode of the last redispatched event + int redispatch_scancode = 0; + bool delegate_handled = false; + TestKeyboardKeyHandler handler([&redispatch_scancode](UINT cInputs, + LPINPUT pInputs, + int cbSize) -> UINT { + EXPECT_TRUE(cbSize > 0); + redispatch_scancode = pInputs->ki.wScan; + return 1; + }); + + auto delegate1 = std::make_unique(1, &hook_history); + CallbackHandler& delegate1_handler = delegate1->callback_handler; + handler.AddDelegate(std::move(delegate1)); - /// Test 2: A delegate responds true + auto delegate2 = std::make_unique(2, &hook_history); + CallbackHandler& delegate2_handler = delegate2->callback_handler; + handler.AddDelegate(std::move(delegate2)); + + /// Test 1: One delegate responds true, the other false delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, L'a', false, false); @@ -218,19 +267,20 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoAsyncDelegates) { EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); EXPECT_EQ(hook_history.back().was_down, false); + EXPECT_EQ(handler.HasPending(), true); + hook_history.back().callback(true); EXPECT_EQ(redispatch_scancode, 0); hook_history.front().callback(false); EXPECT_EQ(redispatch_scancode, 0); + EXPECT_EQ(handler.HasPending(), false); redispatch_scancode = 0; hook_history.clear(); - /// Test 3: All delegates respond false + /// Test 2: All delegates respond false - // Also this is the same event as the previous one, to test that handled - // events are cleared from the pending list. delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, L'a', false, false); EXPECT_EQ(delegate_handled, true); @@ -243,111 +293,50 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoAsyncDelegates) { EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); EXPECT_EQ(hook_history.back().was_down, false); + EXPECT_EQ(handler.HasPending(), true); + hook_history.front().callback(false); EXPECT_EQ(redispatch_scancode, 0); hook_history.back().callback(false); EXPECT_EQ(redispatch_scancode, kHandledScanCode); + EXPECT_EQ(handler.HasPending(), true); EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, L'a', false, false), false); -} - -TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithAsyncAndSyncDelegates) { - std::list hook_history; - - // Capture the scancode of the last redispatched event - int redispatch_scancode = 0; - bool delegate_handled = false; - KeyboardKeyHandler handler([&redispatch_scancode](UINT cInputs, - LPINPUT pInputs, - int cbSize) -> UINT { - EXPECT_TRUE(cbSize > 0); - redispatch_scancode = pInputs->ki.wScan; - return 1; - }); - // Add one delegate - bool delegate1_is_async = true; - auto delegate1 = std::make_unique(1, &hook_history, - [&delegate1_is_async]() { return delegate1_is_async; }); - handler.AddDelegate(std::move(delegate1)); - - /// Test 1: The only delegate is sync. - - delegate1_is_async = false; - delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, - WM_KEYDOWN, L'a', false, false); - EXPECT_EQ(delegate_handled, false); - EXPECT_EQ(redispatch_scancode, 0); - EXPECT_EQ(hook_history.size(), 0); - // Add another delegate - bool delegate2_is_async = true; - auto delegate2 = std::make_unique(2, &hook_history, - [&delegate2_is_async]() { return delegate2_is_async; }); - handler.AddDelegate(std::move(delegate2)); - - redispatch_scancode = 0; + EXPECT_EQ(handler.HasPending(), false); hook_history.clear(); - - /// Test 2: Both delegates are sync - - // Also this is the same event as the previous one, to test that handled - // events are cleared from the pending list. - - delegate1_is_async = false; - delegate2_is_async = false; - delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, - WM_KEYDOWN, L'a', false, false); - EXPECT_EQ(delegate_handled, false); - EXPECT_EQ(redispatch_scancode, 0); - EXPECT_EQ(hook_history.size(), 0); - redispatch_scancode = 0; - hook_history.clear(); - /// Test 3: Only one delegate is sync, the other responds false + /// Test 3: All delegates responds true - // Also this is the same event as the previous one, to test that handled - // events are cleared from the pending list. - delegate1_is_async = true; - delegate2_is_async = false; delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, L'a', false, false); EXPECT_EQ(delegate_handled, true); EXPECT_EQ(redispatch_scancode, 0); - EXPECT_EQ(hook_history.size(), 1); + EXPECT_EQ(hook_history.size(), 2); EXPECT_EQ(hook_history.front().delegate_id, 1); EXPECT_EQ(hook_history.front().scancode, kHandledScanCode); EXPECT_EQ(hook_history.front().was_down, false); + EXPECT_EQ(hook_history.back().delegate_id, 2); + EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); + EXPECT_EQ(hook_history.back().was_down, false); - hook_history.front().callback(false); - EXPECT_EQ(redispatch_scancode, kHandledScanCode); - - EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, - L'a', false, false), - false); - redispatch_scancode = 0; - hook_history.clear(); - - /// Test 4: Only one delegate is sync, the other responds true + EXPECT_EQ(handler.HasPending(), true); - // Also this is the same event as the previous one, to test that handled - // events are cleared from the pending list. - delegate1_is_async = true; - delegate2_is_async = false; - delegate_handled = handler.KeyboardHook(nullptr, 64, kHandledScanCode, - WM_KEYDOWN, L'a', false, false); - EXPECT_EQ(delegate_handled, true); + hook_history.back().callback(true); EXPECT_EQ(redispatch_scancode, 0); - EXPECT_EQ(hook_history.size(), 1); - EXPECT_EQ(hook_history.front().delegate_id, 1); - EXPECT_EQ(hook_history.front().scancode, kHandledScanCode); - EXPECT_EQ(hook_history.front().was_down, false); + // Only resolve after everyone has responded + EXPECT_EQ(handler.HasPending(), true); hook_history.front().callback(true); EXPECT_EQ(redispatch_scancode, 0); + + EXPECT_EQ(handler.HasPending(), false); + redispatch_scancode = 0; + hook_history.clear(); } } // namespace testing diff --git a/shell/platform/windows/testing/mock_win32_window.h b/shell/platform/windows/testing/mock_win32_window.h index df1e5731e3454..0ae73465148c7 100644 --- a/shell/platform/windows/testing/mock_win32_window.h +++ b/shell/platform/windows/testing/mock_win32_window.h @@ -44,6 +44,7 @@ class MockWin32Window : public Win32Window { MOCK_METHOD0(OnComposeBegin, void()); MOCK_METHOD0(OnComposeEnd, void()); MOCK_METHOD2(OnComposeChange, void(const std::u16string&, int)); + MOCK_METHOD4(DefaultWindowProc, LRESULT(HWND, UINT, WPARAM, LPARAM)); }; } // namespace testing diff --git a/shell/platform/windows/win32_flutter_window_unittests.cc b/shell/platform/windows/win32_flutter_window_unittests.cc index f4f9df387a60f..6730f5dd15544 100644 --- a/shell/platform/windows/win32_flutter_window_unittests.cc +++ b/shell/platform/windows/win32_flutter_window_unittests.cc @@ -30,11 +30,11 @@ namespace { // given. static LPARAM CreateKeyEventLparam(USHORT ScanCode, bool extended = false, + bool PreviousKeyState = 1, USHORT RepeatCount = 1, bool ContextCode = 0, - bool WasDown = 1, bool TransitionState = 1) { - return ((LPARAM(TransitionState) << 31) | (LPARAM(WasDown) << 30) | + return ((LPARAM(TransitionState) << 31) | (LPARAM(PreviousKeyState) << 30) | (LPARAM(ContextCode) << 29) | (LPARAM(extended ? 0x1 : 0x0) << 24) | (LPARAM(ScanCode) << 16) | LPARAM(RepeatCount)); } @@ -52,8 +52,8 @@ struct SimulatedEvent { class SpyKeyboardKeyHandler : public KeyboardHandlerBase { public: SpyKeyboardKeyHandler(flutter::BinaryMessenger* messenger, - KeyboardKeyHandler::EventRedispatcher delegate) { - real_implementation_ = std::make_unique(delegate); + KeyboardKeyHandler::EventRedispatcher redispatch_event) { + real_implementation_ = std::make_unique(redispatch_event); real_implementation_->AddDelegate( std::make_unique(messenger)); ON_CALL(*this, KeyboardHook(_, _, _, _, _, _, _)) @@ -146,6 +146,7 @@ class MockWin32FlutterWindow : public Win32FlutterWindow { MOCK_METHOD0(OnPointerLeave, void()); MOCK_METHOD0(OnSetCursor, void()); MOCK_METHOD2(OnScroll, void(double, double)); + MOCK_METHOD4(DefaultWindowProc, LRESULT(HWND, UINT, WPARAM, LPARAM)); }; // A FlutterWindowsView that overrides the RegisterKeyboardHandlers function @@ -289,6 +290,8 @@ TEST(Win32FlutterWindowTest, CreateDestroy) { // Tests key event propagation of non-printable, non-modifier key down events. TEST(Win32FlutterWindowTest, NonPrintableKeyDownPropagation) { + ::testing::InSequence in_sequence; + constexpr WPARAM virtual_key = VK_LEFT; constexpr WPARAM scan_code = 10; constexpr char32_t character = 0; @@ -299,7 +302,7 @@ TEST(Win32FlutterWindowTest, NonPrintableKeyDownPropagation) { TestFlutterWindowsView flutter_windows_view( std::move(window_binding_handler), virtual_key, false /* is_printable */); win32window.SetView(&flutter_windows_view); - LPARAM lparam = CreateKeyEventLparam(scan_code); + LPARAM lparam = CreateKeyEventLparam(scan_code, false /* extended */, false /* PrevState */); // Test an event not handled by the framework { @@ -307,13 +310,17 @@ TEST(Win32FlutterWindowTest, NonPrintableKeyDownPropagation) { flutter_windows_view.SetEngine(std::move(GetTestEngine())); EXPECT_CALL(*flutter_windows_view.key_event_handler, KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, - false /* extended */, true)) + false /* extended */, _)) .Times(2) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.text_input_plugin, KeyboardHook(_, _, _, _, _, _, _)) .Times(1) .RetiresOnSaturation(); + EXPECT_CALL(win32window, + DefaultWindowProc(_, _, _, _)) + .Times(0) + .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.key_event_handler, TextHook(_, _)) .Times(0); EXPECT_CALL(*flutter_windows_view.text_input_plugin, TextHook(_, _)) @@ -328,7 +335,7 @@ TEST(Win32FlutterWindowTest, NonPrintableKeyDownPropagation) { test_response = true; EXPECT_CALL(*flutter_windows_view.key_event_handler, KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, - false /* extended */, true)) + false /* extended */, false /* PrevState */)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.text_input_plugin, @@ -344,6 +351,8 @@ TEST(Win32FlutterWindowTest, NonPrintableKeyDownPropagation) { // differ from non-printable characters in that they follow a different code // path in the WndProc (HandleMessage), producing a follow-on WM_CHAR event. TEST(Win32FlutterWindowTest, CharKeyDownPropagation) { + // ::testing::InSequence in_sequence; + constexpr WPARAM virtual_key = 65; // The "A" key, which produces a character constexpr WPARAM scan_code = 30; constexpr char32_t character = 65; @@ -354,7 +363,7 @@ TEST(Win32FlutterWindowTest, CharKeyDownPropagation) { TestFlutterWindowsView flutter_windows_view( std::move(window_binding_handler), virtual_key, true /* is_printable */); win32window.SetView(&flutter_windows_view); - LPARAM lparam = CreateKeyEventLparam(scan_code); + LPARAM lparam = CreateKeyEventLparam(scan_code, false /* extended */, true /* PrevState */); // Test an event not handled by the framework { @@ -362,7 +371,7 @@ TEST(Win32FlutterWindowTest, CharKeyDownPropagation) { flutter_windows_view.SetEngine(std::move(GetTestEngine())); EXPECT_CALL(*flutter_windows_view.key_event_handler, KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, - false, false)) + false, true)) .Times(2) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.text_input_plugin, @@ -380,13 +389,14 @@ TEST(Win32FlutterWindowTest, CharKeyDownPropagation) { EXPECT_EQ(win32window.InjectWindowMessage(WM_CHAR, virtual_key, lparam), 0); flutter_windows_view.InjectPendingEvents(&win32window); } + return; // Test an event handled by the framework { test_response = true; EXPECT_CALL(*flutter_windows_view.key_event_handler, KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, - false /* is_printable */, false)) + false /* is_printable */, true)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.text_input_plugin, diff --git a/shell/platform/windows/win32_window.cc b/shell/platform/windows/win32_window.cc index d55f097c04ca2..b5f0c998185bf 100644 --- a/shell/platform/windows/win32_window.cc +++ b/shell/platform/windows/win32_window.cc @@ -336,8 +336,9 @@ Win32Window::HandleMessage(UINT const message, if (keycode_for_char_message_ != 0) { const unsigned int scancode = (lparam >> 16) & 0xff; const bool extended = ((lparam >> 24) & 0x01) == 0x01; + const bool was_down = lparam & 0x40000000; bool handled = OnKey(keycode_for_char_message_, scancode, WM_KEYDOWN, - code_point, extended, false); + code_point, extended, was_down); keycode_for_char_message_ = 0; if (handled) { // If the OnKey handler handles the message, then return so we don't @@ -430,4 +431,8 @@ Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { GetWindowLongPtr(window, GWLP_USERDATA)); } +LRESULT Win32Window::DefaultWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + return DefWindowProc(hWnd, Msg, wParam, lParam); +} + } // namespace flutter diff --git a/shell/platform/windows/win32_window.h b/shell/platform/windows/win32_window.h index 483cfd41e76e1..69d9137ded1de 100644 --- a/shell/platform/windows/win32_window.h +++ b/shell/platform/windows/win32_window.h @@ -150,6 +150,9 @@ class Win32Window { UINT GetCurrentHeight(); + protected: + LRESULT DefaultWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + private: // Release OS resources asociated with window. void Destroy(); diff --git a/shell/platform/windows/win32_window_unittests.cc b/shell/platform/windows/win32_window_unittests.cc index 5788bbfefe161..38a1b068fd55d 100644 --- a/shell/platform/windows/win32_window_unittests.cc +++ b/shell/platform/windows/win32_window_unittests.cc @@ -15,9 +15,9 @@ namespace { // given. static LPARAM CreateKeyEventLparam(USHORT ScanCode, bool extended = false, + bool PreviousKeyState = 1, USHORT RepeatCount = 1, bool ContextCode = 0, - bool PreviousKeyState = 1, bool TransitionState = 1) { return ((LPARAM(TransitionState) << 31) | (LPARAM(PreviousKeyState) << 30) | (LPARAM(ContextCode) << 29) | (LPARAM(extended ? 0x1 : 0x0) << 24) | @@ -75,11 +75,11 @@ TEST(MockWin32Window, KeyDownPrintable) { MockWin32Window window; LPARAM lparam = CreateKeyEventLparam(30); // OnKey shouldn't be called until the WM_CHAR message. - EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false, false)).Times(0); + EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false, true)).Times(0); // send a "A" key down event. window.InjectWindowMessage(WM_KEYDOWN, 65, lparam); - EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false, false)).Times(1); + EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false, true)).Times(1); EXPECT_CALL(window, OnText(_)).Times(1); window.InjectWindowMessage(WM_CHAR, 65, lparam); } From fc9ebaa7bf3c23f59999cb5284ee5646fe1ea636 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 12 Feb 2021 02:42:39 -0800 Subject: [PATCH 61/82] Refactor --- .../platform/windows/keyboard_key_handler.cc | 162 +++++++++--------- shell/platform/windows/keyboard_key_handler.h | 56 ++++-- 2 files changed, 122 insertions(+), 96 deletions(-) diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index a6be51cbf8a34..5f26942b204a4 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -32,33 +32,13 @@ static constexpr char kKeyDown[] = "keydown"; // emitting a warning on the console about unhandled events. static constexpr int kMaxPendingEvents = 1000; -// This uses event data instead of generating a serial number because -// information can't be attached to the redispatched events, so it has to be -// possible to compute an ID from the identifying data in the event when it is -// received again in order to differentiate between events that are new, and -// events that have been redispatched. -// -// Another alternative would be to compute a checksum from all the data in the -// event (just compute it over the bytes in the struct, probably skipping -// timestamps), but the fields used below are enough to differentiate them, and -// since Windows does some processing on the events (coming up with virtual key -// codes, setting timestamps, etc.), it's not clear that the redispatched -// events would have the same checksums. -uint64_t CalculateEventId(int scancode, int action, bool extended) { - // Calculate a key event ID based on the scan code of the key pressed, - // and the flags we care about. - return scancode | (((action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0) | - (extended ? KEYEVENTF_EXTENDEDKEY : 0x0)) - << 16); -} - } // namespace KeyboardKeyHandler::KeyboardKeyHandlerDelegate::~KeyboardKeyHandlerDelegate() = default; KeyboardKeyHandler::KeyboardKeyHandler(EventRedispatcher redispatch_event) - : redispatch_event_(redispatch_event) { + : redispatch_event_(redispatch_event), last_sequence_id_(1) { assert(redispatch_event_ != nullptr); } @@ -76,46 +56,28 @@ size_t KeyboardKeyHandler::PendingAmount() { return pending_events_.size(); } -const KeyboardKeyHandler::PendingEvent* KeyboardKeyHandler::FindPendingEvent( - uint64_t id) { - if (pending_events_.empty()) { - return nullptr; - } - for (auto iter = pending_events_.begin(); iter != pending_events_.end(); - ++iter) { - if ((*iter)->id == id) { - return iter->get(); - } - } - return nullptr; -} +void KeyboardKeyHandler::RedispatchEvent(std::unique_ptr event) { + // printf("# Redispatch dwFlags %08lx\n", pending->dwFlags); + uint8_t scancode = event->scancode; + char32_t character = event->character; + + INPUT input_event{ + .type = INPUT_KEYBOARD, + .ki = KEYBDINPUT{ + .wVk = 0, + .wScan = static_cast(event->scancode), + .dwFlags = static_cast(KEYEVENTF_SCANCODE | (event->extended ? KEYEVENTF_EXTENDEDKEY : 0x0) | + (event->action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0)), + }, + }; -void KeyboardKeyHandler::RemovePendingEvent(uint64_t id) { - for (auto iter = pending_events_.begin(); iter != pending_events_.end(); - ++iter) { - if ((*iter)->id == id) { - pending_events_.erase(iter); - return; - } - } - std::cerr << "Tried to remove pending event with id " << id - << ", but the event was not found." << std::endl; -} + redispatched_events_.push_back(std::move(event)); -void KeyboardKeyHandler::RedispatchEvent(const PendingEvent* pending) { - KEYBDINPUT ki{ - .wVk = 0, - .wScan = static_cast(pending->scancode), - .dwFlags = pending->dwFlags, - }; - INPUT input_event; - input_event.type = INPUT_KEYBOARD; - input_event.ki = std::move(ki); UINT accepted = redispatch_event_(1, &input_event, sizeof(input_event)); if (accepted != 1) { std::cerr << "Unable to synthesize event for unhandled keyboard event " "with scancode " - << pending->scancode << " (character " << pending->character + << scancode << " (character " << character << ")" << std::endl; } } @@ -127,36 +89,42 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, char32_t character, bool extended, bool was_down) { - const uint64_t id = CalculateEventId(scancode, action, extended); - if (FindPendingEvent(id) != nullptr) { - // Don't pass messages that we synthesized to the framework again. - RemovePendingEvent(id); + std::unique_ptr incoming = std::make_unique(PendingEvent{ + .key = static_cast(key), + .scancode = static_cast(scancode), + .action = static_cast(action), + .character = character, + .extended = extended, + .was_down = was_down, + }); + incoming->hash = ComputeEventHash(*incoming); + + if (RemoveRedispatchedEvent(*incoming)) { return false; } + uint64_t sequence_id = ++last_sequence_id_; + incoming->sequence_id = sequence_id; + incoming->unreplied = delegates_.size(); + incoming->any_handled = false; + + printf("#### EVENT[%lld] key %x scancode %x action %x char %x ext %d wasDown %d\n", + incoming->sequence_id, key, scancode, action, character, extended, was_down);fflush(stdout); + if (pending_events_.size() > kMaxPendingEvents) { std::cerr << "There are " << pending_events_.size() << " keyboard events that have not yet received a response from the " << "framework. Are responses being sent?" << std::endl; } - pending_events_.push_back(std::make_unique(PendingEvent{ - .id = id, - .scancode = scancode, - .character = character, - .dwFlags = static_cast( - KEYEVENTF_SCANCODE | (extended ? KEYEVENTF_EXTENDEDKEY : 0x0) | - (action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0)), - .unreplied = delegates_.size(), - .any_handled = false, - })); - PendingEvent& pending_event = *pending_events_.back(); + pending_events_.push_back(std::move(incoming)); for (const auto& delegate : delegates_) { delegate->KeyboardHook( key, scancode, action, character, extended, was_down, - [pending_event = &pending_event, this](bool handled) { - ResolvePendingEvent(pending_event, handled); + [sequence_id, this](bool handled) { + printf("#### RET[%lld] handled %d\n", sequence_id, handled);fflush(stdout); + ResolvePendingEvent(sequence_id, handled); }); } @@ -169,18 +137,40 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, return true; } -void KeyboardKeyHandler::ResolvePendingEvent(PendingEvent* pending_event, - bool handled) { - pending_event->unreplied -= 1; - assert(pending_event->unreplied >= 0); - pending_event->any_handled = pending_event->any_handled || handled; - if (pending_event->unreplied == 0) { - if (!pending_event->any_handled) { - RedispatchEvent(pending_event); - } else { - RemovePendingEvent(pending_event->id); +bool KeyboardKeyHandler::RemoveRedispatchedEvent(const PendingEvent& incoming) { + for (auto iter = redispatched_events_.begin(); iter != redispatched_events_.end(); + ++iter) { + if ((*iter)->hash == incoming.hash) { + redispatched_events_.erase(iter); + printf("#### Redispatched[#%lld] remain %llu\n", incoming.hash, redispatched_events_.size());fflush(stdout); + return true; } } + return false;; +} + +void KeyboardKeyHandler::ResolvePendingEvent(uint64_t sequence_id, bool handled) { + // Find the pending event + for (auto iter = pending_events_.begin(); iter != pending_events_.end(); + ++iter) { + if ((*iter)->sequence_id == sequence_id) { + std::unique_ptr event = std::move(*iter); + event->any_handled = event->any_handled || handled; + event->unreplied -= 1; + assert(event->unreplied >= 0); + // If all delegates have replied, redispatch if no one handled. + if (event->unreplied == 0) { + pending_events_.erase(iter); + if (!event->any_handled) { + RedispatchEvent(std::move(event)); + } + } + // Return here; |iter| can't do ++ after erase. + return; + } + } + // The pending event should always be found. + assert(false); } void KeyboardKeyHandler::ComposeBeginHook() { @@ -196,4 +186,12 @@ void KeyboardKeyHandler::ComposeChangeHook(const std::u16string& text, // Ignore. } +uint64_t KeyboardKeyHandler::ComputeEventHash(const PendingEvent& event) { + // Calculate a key event ID based on the scan code of the key pressed, + // and the flags we care about. + return event.scancode | (((event.action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0) | + (event.extended ? KEYEVENTF_EXTENDEDKEY : 0x0)) + << 16); +} + } // namespace flutter diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index e857a941ee73b..410225999c4e7 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -107,28 +107,32 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { private: struct PendingEvent { - uint64_t id; - int scancode; + uint32_t key; + uint8_t scancode; + uint32_t action; char32_t character; - DWORD dwFlags; - DWORD lParam; + bool extended; + bool was_down; + // Self-incrementing ID attached to an event sent to the framework. + uint64_t sequence_id; // The number of delegates that haven't replied. size_t unreplied; // Whether any replied delegates reported true (handled). bool any_handled; + + // A value calculated out of critical event information that can be used + // to identify redispatched events. + uint64_t hash; }; - const PendingEvent* FindPendingEvent(uint64_t id); - void HandleResponse(bool handled, - uint64_t id, - int action, - bool extended, - int scancode, - int character); - void RedispatchEvent(const PendingEvent* pending); - void RemovePendingEvent(uint64_t id); - void ResolvePendingEvent(PendingEvent* pending, bool handled); + // Find an event in the redispatch list that matches the given one. + // + // If an matching event is found, removes the matching event from the + // redispatch list, and returns true. Otherwise, returns false; + bool RemoveRedispatchedEvent(const PendingEvent& incoming); + void RedispatchEvent(std::unique_ptr event); + void ResolvePendingEvent(uint64_t sequence_id, bool handled); std::vector> delegates_; @@ -136,8 +140,32 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { // yet received a response. std::deque> pending_events_; + // The queue of key events that have been redispatcehd to the system but have + // not yet been received for a second time. + std::deque> redispatched_events_; + + // The last sequence_id attached to the event sent to the framework. + uint64_t last_sequence_id_; + // A function used to redispatch synthesized events. EventRedispatcher redispatch_event_; + + // Calculate a hash based on event data for fast comparison for a redispatched + // event. + // + // This uses event data instead of generating a serial number because + // information can't be attached to the redispatched events, so it has to be + // possible to compute an ID from the identifying data in the event when it is + // received again in order to differentiate between events that are new, and + // events that have been redispatched. + // + // Another alternative would be to compute a checksum from all the data in the + // event (just compute it over the bytes in the struct, probably skipping + // timestamps), but the fields used below are enough to differentiate them, and + // since Windows does some processing on the events (coming up with virtual key + // codes, setting timestamps, etc.), it's not clear that the redispatched + // events would have the same checksums. + static uint64_t ComputeEventHash(const PendingEvent& event); }; } // namespace flutter From 033ddbd4b2de121b7168002baf557e30e8044aa1 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 12 Feb 2021 03:20:52 -0800 Subject: [PATCH 62/82] Fix tests --- .../platform/windows/keyboard_key_handler.cc | 25 ++++++--------- shell/platform/windows/keyboard_key_handler.h | 2 +- .../windows/keyboard_key_handler_unittests.cc | 32 +++++++++---------- 3 files changed, 27 insertions(+), 32 deletions(-) diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index 5f26942b204a4..a581bcc2ba018 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -52,12 +52,11 @@ void KeyboardKeyHandler::AddDelegate( delegates_.push_back(std::move(delegate)); } -size_t KeyboardKeyHandler::PendingAmount() { - return pending_events_.size(); +size_t KeyboardKeyHandler::RedispatchedAmount() { + return redispatched_events_.size(); } void KeyboardKeyHandler::RedispatchEvent(std::unique_ptr event) { - // printf("# Redispatch dwFlags %08lx\n", pending->dwFlags); uint8_t scancode = event->scancode; char32_t character = event->character; @@ -108,9 +107,6 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, incoming->unreplied = delegates_.size(); incoming->any_handled = false; - printf("#### EVENT[%lld] key %x scancode %x action %x char %x ext %d wasDown %d\n", - incoming->sequence_id, key, scancode, action, character, extended, was_down);fflush(stdout); - if (pending_events_.size() > kMaxPendingEvents) { std::cerr << "There are " << pending_events_.size() @@ -123,7 +119,6 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, delegate->KeyboardHook( key, scancode, action, character, extended, was_down, [sequence_id, this](bool handled) { - printf("#### RET[%lld] handled %d\n", sequence_id, handled);fflush(stdout); ResolvePendingEvent(sequence_id, handled); }); } @@ -142,7 +137,6 @@ bool KeyboardKeyHandler::RemoveRedispatchedEvent(const PendingEvent& incoming) { ++iter) { if ((*iter)->hash == incoming.hash) { redispatched_events_.erase(iter); - printf("#### Redispatched[#%lld] remain %llu\n", incoming.hash, redispatched_events_.size());fflush(stdout); return true; } } @@ -154,15 +148,16 @@ void KeyboardKeyHandler::ResolvePendingEvent(uint64_t sequence_id, bool handled) for (auto iter = pending_events_.begin(); iter != pending_events_.end(); ++iter) { if ((*iter)->sequence_id == sequence_id) { - std::unique_ptr event = std::move(*iter); - event->any_handled = event->any_handled || handled; - event->unreplied -= 1; - assert(event->unreplied >= 0); + PendingEvent& event = **iter; + event.any_handled = event.any_handled || handled; + event.unreplied -= 1; + assert(event.unreplied >= 0); // If all delegates have replied, redispatch if no one handled. - if (event->unreplied == 0) { + if (event.unreplied == 0) { + auto event_ptr = std::move(*iter); pending_events_.erase(iter); - if (!event->any_handled) { - RedispatchEvent(std::move(event)); + if (!event_ptr->any_handled) { + RedispatchEvent(std::move(event_ptr)); } } // Return here; |iter| can't do ++ after erase. diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index 410225999c4e7..9da64a4740a9d 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -103,7 +103,7 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { void ComposeChangeHook(const std::u16string& text, int cursor_pos) override; protected: - size_t PendingAmount(); + size_t RedispatchedAmount(); private: struct PendingEvent { diff --git a/shell/platform/windows/keyboard_key_handler_unittests.cc b/shell/platform/windows/keyboard_key_handler_unittests.cc index ce2beb0963fe8..7c636f2d223f2 100644 --- a/shell/platform/windows/keyboard_key_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_handler_unittests.cc @@ -89,8 +89,8 @@ class TestKeyboardKeyHandler : public KeyboardKeyHandler { explicit TestKeyboardKeyHandler(EventRedispatcher redispatch_event) : KeyboardKeyHandler(redispatch_event) {} - bool HasPending() { - return PendingAmount() > 0; + bool HasRedispatched() { + return RedispatchedAmount() > 0; } }; @@ -125,11 +125,11 @@ TEST(KeyboardKeyHandlerTest, SingleDelegateWithAsyncResponds) { EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); EXPECT_EQ(hook_history.back().was_down, true); - EXPECT_EQ(handler.HasPending(), true); + EXPECT_EQ(handler.HasRedispatched(), false); hook_history.back().callback(true); EXPECT_EQ(redispatch_scancode, 0); - EXPECT_EQ(handler.HasPending(), false); + EXPECT_EQ(handler.HasRedispatched(), false); hook_history.clear(); /// Test 2: Two events that are unhandled by the framework @@ -168,7 +168,7 @@ TEST(KeyboardKeyHandlerTest, SingleDelegateWithAsyncResponds) { false, false), false); - EXPECT_EQ(handler.HasPending(), false); + EXPECT_EQ(handler.HasRedispatched(), false); hook_history.clear(); redispatch_scancode = 0; } @@ -204,7 +204,7 @@ TEST(KeyboardKeyHandlerTest, SingleDelegateWithSyncResponds) { EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); EXPECT_EQ(hook_history.back().was_down, false); - EXPECT_EQ(handler.HasPending(), false); + EXPECT_EQ(handler.HasRedispatched(), false); hook_history.clear(); /// Test 2: An event unhandled by the framework @@ -219,14 +219,14 @@ TEST(KeyboardKeyHandlerTest, SingleDelegateWithSyncResponds) { EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); EXPECT_EQ(hook_history.back().was_down, false); - EXPECT_EQ(handler.HasPending(), true); + EXPECT_EQ(handler.HasRedispatched(), true); // Resolve the event EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, L'a', false, false), false); - EXPECT_EQ(handler.HasPending(), false); + EXPECT_EQ(handler.HasRedispatched(), false); hook_history.clear(); redispatch_scancode = 0; } @@ -267,7 +267,7 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoAsyncDelegates) { EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); EXPECT_EQ(hook_history.back().was_down, false); - EXPECT_EQ(handler.HasPending(), true); + EXPECT_EQ(handler.HasRedispatched(), false); hook_history.back().callback(true); EXPECT_EQ(redispatch_scancode, 0); @@ -275,7 +275,7 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoAsyncDelegates) { hook_history.front().callback(false); EXPECT_EQ(redispatch_scancode, 0); - EXPECT_EQ(handler.HasPending(), false); + EXPECT_EQ(handler.HasRedispatched(), false); redispatch_scancode = 0; hook_history.clear(); @@ -293,7 +293,7 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoAsyncDelegates) { EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); EXPECT_EQ(hook_history.back().was_down, false); - EXPECT_EQ(handler.HasPending(), true); + EXPECT_EQ(handler.HasRedispatched(), false); hook_history.front().callback(false); EXPECT_EQ(redispatch_scancode, 0); @@ -301,12 +301,12 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoAsyncDelegates) { hook_history.back().callback(false); EXPECT_EQ(redispatch_scancode, kHandledScanCode); - EXPECT_EQ(handler.HasPending(), true); + EXPECT_EQ(handler.HasRedispatched(), true); EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, L'a', false, false), false); - EXPECT_EQ(handler.HasPending(), false); + EXPECT_EQ(handler.HasRedispatched(), false); hook_history.clear(); redispatch_scancode = 0; @@ -324,17 +324,17 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoAsyncDelegates) { EXPECT_EQ(hook_history.back().scancode, kHandledScanCode); EXPECT_EQ(hook_history.back().was_down, false); - EXPECT_EQ(handler.HasPending(), true); + EXPECT_EQ(handler.HasRedispatched(), false); hook_history.back().callback(true); EXPECT_EQ(redispatch_scancode, 0); // Only resolve after everyone has responded - EXPECT_EQ(handler.HasPending(), true); + EXPECT_EQ(handler.HasRedispatched(), false); hook_history.front().callback(true); EXPECT_EQ(redispatch_scancode, 0); - EXPECT_EQ(handler.HasPending(), false); + EXPECT_EQ(handler.HasRedispatched(), false); redispatch_scancode = 0; hook_history.clear(); } From 009237403abe473583f26a7f64146e276e69fd70 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 12 Feb 2021 15:40:17 -0800 Subject: [PATCH 63/82] Add regression test --- .../windows/keyboard_key_handler_unittests.cc | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/shell/platform/windows/keyboard_key_handler_unittests.cc b/shell/platform/windows/keyboard_key_handler_unittests.cc index 7c636f2d223f2..b0e3469fa723f 100644 --- a/shell/platform/windows/keyboard_key_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_handler_unittests.cc @@ -339,5 +339,69 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoAsyncDelegates) { hook_history.clear(); } +// Regression test for a crash in an earlier implementation. +// +// In real life, the framework responses slowly. The next real event might arrive +// earlier than the framework response, and if the 2nd event is identical to the +// one waiting for response, an earlier implementation will crash upon the +// response. +TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithSlowFrameworkResponse) { + std::list hook_history; + + // Capture the scancode of the last redispatched event + int redispatch_scancode = 0; + bool delegate_handled = false; + TestKeyboardKeyHandler handler([&redispatch_scancode](UINT cInputs, + LPINPUT pInputs, + int cbSize) -> UINT { + EXPECT_TRUE(cbSize > 0); + redispatch_scancode = pInputs->ki.wScan; + return 1; + }); + + auto delegate1 = std::make_unique(1, &hook_history); + CallbackHandler& delegate1_handler = delegate1->callback_handler; + handler.AddDelegate(std::move(delegate1)); + + // The first native event. + EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, + WM_KEYDOWN, L'a', false, true), true); + + // The second identical native event, received between the first and its + // framework response. + EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, + WM_KEYDOWN, L'a', false, true), true); + EXPECT_EQ(redispatch_scancode, 0); + EXPECT_EQ(hook_history.size(), 2); + + EXPECT_EQ(handler.HasRedispatched(), false); + + // The first response. + hook_history.front().callback(false); + EXPECT_EQ(redispatch_scancode, kHandledScanCode); + EXPECT_EQ(handler.HasRedispatched(), true); + + // Redispatch the first event. + EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, + L'a', false, false), + false); + EXPECT_EQ(handler.HasRedispatched(), false); + redispatch_scancode = 0; + + // The second response. + hook_history.back().callback(false); + EXPECT_EQ(redispatch_scancode, kHandledScanCode); + EXPECT_EQ(handler.HasRedispatched(), true); + + // Redispatch the second event. + EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, + L'a', false, false), + false); + + EXPECT_EQ(handler.HasRedispatched(), false); + redispatch_scancode = 0; + hook_history.clear(); +} + } // namespace testing } // namespace flutter From a25575f4422432bf0ce1d72eb0bc657100372e45 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Sat, 13 Feb 2021 01:59:42 -0800 Subject: [PATCH 64/82] Renames --- shell/platform/windows/keyboard_key_handler.cc | 18 +++++++++--------- shell/platform/windows/keyboard_key_handler.h | 10 +++++----- .../windows/keyboard_key_handler_unittests.cc | 4 ++-- .../windows/win32_flutter_window_unittests.cc | 12 ++++++------ 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index a581bcc2ba018..7f979197c6367 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -53,7 +53,7 @@ void KeyboardKeyHandler::AddDelegate( } size_t KeyboardKeyHandler::RedispatchedAmount() { - return redispatched_events_.size(); + return pending_redispatches_.size(); } void KeyboardKeyHandler::RedispatchEvent(std::unique_ptr event) { @@ -70,7 +70,7 @@ void KeyboardKeyHandler::RedispatchEvent(std::unique_ptr event) { }, }; - redispatched_events_.push_back(std::move(event)); + pending_redispatches_.push_back(std::move(event)); UINT accepted = redispatch_event_(1, &input_event, sizeof(input_event)); if (accepted != 1) { @@ -107,13 +107,13 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, incoming->unreplied = delegates_.size(); incoming->any_handled = false; - if (pending_events_.size() > kMaxPendingEvents) { + if (pending_responds_.size() > kMaxPendingEvents) { std::cerr - << "There are " << pending_events_.size() + << "There are " << pending_responds_.size() << " keyboard events that have not yet received a response from the " << "framework. Are responses being sent?" << std::endl; } - pending_events_.push_back(std::move(incoming)); + pending_responds_.push_back(std::move(incoming)); for (const auto& delegate : delegates_) { delegate->KeyboardHook( @@ -133,10 +133,10 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, } bool KeyboardKeyHandler::RemoveRedispatchedEvent(const PendingEvent& incoming) { - for (auto iter = redispatched_events_.begin(); iter != redispatched_events_.end(); + for (auto iter = pending_redispatches_.begin(); iter != pending_redispatches_.end(); ++iter) { if ((*iter)->hash == incoming.hash) { - redispatched_events_.erase(iter); + pending_redispatches_.erase(iter); return true; } } @@ -145,7 +145,7 @@ bool KeyboardKeyHandler::RemoveRedispatchedEvent(const PendingEvent& incoming) { void KeyboardKeyHandler::ResolvePendingEvent(uint64_t sequence_id, bool handled) { // Find the pending event - for (auto iter = pending_events_.begin(); iter != pending_events_.end(); + for (auto iter = pending_responds_.begin(); iter != pending_responds_.end(); ++iter) { if ((*iter)->sequence_id == sequence_id) { PendingEvent& event = **iter; @@ -155,7 +155,7 @@ void KeyboardKeyHandler::ResolvePendingEvent(uint64_t sequence_id, bool handled) // If all delegates have replied, redispatch if no one handled. if (event.unreplied == 0) { auto event_ptr = std::move(*iter); - pending_events_.erase(iter); + pending_responds_.erase(iter); if (!event_ptr->any_handled) { RedispatchEvent(std::move(event_ptr)); } diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index 9da64a4740a9d..1163c74ddee20 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -138,16 +138,16 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { // The queue of key events that have been sent to the framework but have not // yet received a response. - std::deque> pending_events_; + std::deque> pending_responds_; - // The queue of key events that have been redispatcehd to the system but have + // The queue of key events that have been redispatched to the system but have // not yet been received for a second time. - std::deque> redispatched_events_; + std::deque> pending_redispatches_; - // The last sequence_id attached to the event sent to the framework. + // The sequence_id attached to the last event sent to the framework. uint64_t last_sequence_id_; - // A function used to redispatch synthesized events. + // The callback used to redispatch synthesized events. EventRedispatcher redispatch_event_; // Calculate a hash based on event data for fast comparison for a redispatched diff --git a/shell/platform/windows/keyboard_key_handler_unittests.cc b/shell/platform/windows/keyboard_key_handler_unittests.cc index b0e3469fa723f..88e79d07cd118 100644 --- a/shell/platform/windows/keyboard_key_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_handler_unittests.cc @@ -231,7 +231,7 @@ TEST(KeyboardKeyHandlerTest, SingleDelegateWithSyncResponds) { redispatch_scancode = 0; } -TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoAsyncDelegates) { +TEST(KeyboardKeyHandlerTest, WithTwoAsyncDelegates) { std::list hook_history; // Capture the scancode of the last redispatched event @@ -345,7 +345,7 @@ TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithTwoAsyncDelegates) { // earlier than the framework response, and if the 2nd event is identical to the // one waiting for response, an earlier implementation will crash upon the // response. -TEST(KeyboardKeyHandlerTest, BehavesCorrectlyWithSlowFrameworkResponse) { +TEST(KeyboardKeyHandlerTest, WithSlowFrameworkResponse) { std::list hook_history; // Capture the scancode of the last redispatched event diff --git a/shell/platform/windows/win32_flutter_window_unittests.cc b/shell/platform/windows/win32_flutter_window_unittests.cc index 6730f5dd15544..0a51324904a92 100644 --- a/shell/platform/windows/win32_flutter_window_unittests.cc +++ b/shell/platform/windows/win32_flutter_window_unittests.cc @@ -164,11 +164,11 @@ class TestFlutterWindowsView : public FlutterWindowsView { SpyTextInputPlugin* text_input_plugin; void InjectPendingEvents(MockWin32FlutterWindow* win32window) { - while (pending_events_.size() > 0) { - SimulatedEvent event = pending_events_.front(); + while (pending_responds_.size() > 0) { + SimulatedEvent event = pending_responds_.front(); win32window->InjectWindowMessage(event.message, event.wparam, event.lparam); - pending_events_.pop_front(); + pending_responds_.pop_front(); } } @@ -199,14 +199,14 @@ class TestFlutterWindowsView : public FlutterWindowsView { // simulate it for the test with the key we know is in the test. The // KBDINPUT we're passed doesn't have it filled in (on purpose, so that // Windows will fill it in). - pending_events_.push_back(SimulatedEvent{message, virtual_key_, lparam}); + pending_responds_.push_back(SimulatedEvent{message, virtual_key_, lparam}); if (is_printable_ && (kbdinput.dwFlags & KEYEVENTF_KEYUP) == 0) { - pending_events_.push_back(SimulatedEvent{WM_CHAR, virtual_key_, lparam}); + pending_responds_.push_back(SimulatedEvent{WM_CHAR, virtual_key_, lparam}); } return 1; } - std::deque pending_events_; + std::deque pending_responds_; WPARAM virtual_key_; bool is_printable_; }; From 3c002841153234565a44ee244e54f968fe518aa3 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 16 Feb 2021 16:48:52 -0800 Subject: [PATCH 65/82] processkey test --- ...keyboard_key_embedder_handler_unittests.cc | 82 +++++++++++++++---- 1 file changed, 67 insertions(+), 15 deletions(-) diff --git a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index 6d7e34c572cf2..fd55b005d2a13 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -42,14 +42,16 @@ class TestFlutterKeyEvent : public FlutterKeyEvent { namespace testing { namespace { -constexpr uint64_t kPhysicalKeyA = 0x1e; -constexpr uint64_t kPhysicalNumpad1 = 0x4f; -constexpr uint64_t kPhysicalNumLock = 0x45; - -constexpr uint64_t kLogicalKeyA = 0x41; -constexpr uint64_t kLogicalNumpad1 = 0x61; -constexpr uint64_t kLogicalNumpadEnd = 0x23; -constexpr uint64_t kLogicalNumLock = 0x90; +constexpr uint64_t kScanCodeKeyA = 0x1e; +constexpr uint64_t kScanCodeNumpad1 = 0x4f; +constexpr uint64_t kScanCodeNumLock = 0x45; + +constexpr uint64_t kVirtualKeyA = 0x41; +constexpr uint64_t kVirtualNumpad1 = 0x61; +constexpr uint64_t kVirtualNumpadEnd = 0x23; +constexpr uint64_t kVirtualNumLock = 0x90; + +constexpr uint64_t kVirtualProcessKey = 0xe5; } // namespace // Test the most basic key events. @@ -69,7 +71,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { // Press KeyA. handler->KeyboardHook( - kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', false, false, + kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, false); EXPECT_EQ(results.size(), 1); @@ -87,7 +89,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { // Hold KeyA. handler->KeyboardHook( - kLogicalKeyA, kPhysicalKeyA, WM_KEYDOWN, 'a', false, true, + kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, true, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, true); EXPECT_EQ(results.size(), 1); @@ -105,7 +107,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { // Release KeyA. handler->KeyboardHook( - kLogicalKeyA, kPhysicalKeyA, WM_KEYUP, 0, false, true, + kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 0, false, true, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; @@ -136,7 +138,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { // Press NumPad1. handler->KeyboardHook( - kLogicalNumpad1, kPhysicalNumpad1, WM_KEYDOWN, 0, false, false, + kVirtualNumpad1, kScanCodeNumpad1, WM_KEYDOWN, 0, false, false, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; @@ -149,7 +151,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { // Press NumLock. handler->KeyboardHook( - kLogicalNumLock, kPhysicalNumLock, WM_KEYDOWN, 0, true, false, + kVirtualNumLock, kScanCodeNumLock, WM_KEYDOWN, 0, true, false, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; @@ -162,7 +164,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { // Release NumLock. handler->KeyboardHook( - kLogicalNumLock, kPhysicalNumLock, WM_KEYUP, 0, true, true, + kVirtualNumLock, kScanCodeNumLock, WM_KEYUP, 0, true, true, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; @@ -175,7 +177,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { // Release NumPad1. (The logical key is now NumpadEnd) handler->KeyboardHook( - kLogicalNumpadEnd, kPhysicalNumpad1, WM_KEYUP, 0, false, true, + kVirtualNumpadEnd, kScanCodeNumpad1, WM_KEYUP, 0, false, true, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; @@ -188,6 +190,56 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { } TEST(KeyboardKeyEmbedderHandlerTest, ImeEventsAreIgnored) { + std::vector results; + TestFlutterKeyEvent* event; + bool last_handled = false; + + std::unique_ptr handler = + std::make_unique( + [&results](const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, void* user_data) { + results.emplace_back(event, callback, user_data); + }); + + // Press A in an IME + last_handled = false; + handler->KeyboardHook( + kVirtualProcessKey, kScanCodeKeyA, WM_KEYDOWN, 0, true, false, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, true); + + last_handled = false; + handler->KeyboardHook( + // The up event for an IME press has a normal virtual key. + kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 0, true, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, true); + + // The entire A press does not yield events. + EXPECT_EQ(results.size(), 0); + + // Press A out of an IME + last_handled = false; + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 0, true, false, + [&last_handled](bool handled) { last_handled = handled; }); + // Not decided yet + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); + + last_handled = false; + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 0, true, false, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); } } // namespace testing From 9b698b16e2b12282e76463d995f97bbdacc76825 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 16 Feb 2021 23:41:13 -0800 Subject: [PATCH 66/82] Tests for modifiers --- ...keyboard_key_embedder_handler_unittests.cc | 209 +++++++++++++++++- 1 file changed, 201 insertions(+), 8 deletions(-) diff --git a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index fd55b005d2a13..90d141d2d290a 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -45,13 +45,30 @@ namespace { constexpr uint64_t kScanCodeKeyA = 0x1e; constexpr uint64_t kScanCodeNumpad1 = 0x4f; constexpr uint64_t kScanCodeNumLock = 0x45; +constexpr uint64_t kScanCodeControl = 0x1d; +constexpr uint64_t kScanCodeShiftLeft = 0x2a; +constexpr uint64_t kScanCodeShiftRight = 0x36; +constexpr uint64_t kVirtualProcessKey = 0xe5; constexpr uint64_t kVirtualKeyA = 0x41; constexpr uint64_t kVirtualNumpad1 = 0x61; constexpr uint64_t kVirtualNumpadEnd = 0x23; constexpr uint64_t kVirtualNumLock = 0x90; - -constexpr uint64_t kVirtualProcessKey = 0xe5; +constexpr uint64_t kVirtualControl = 0xa2; +constexpr uint64_t kVirtualShiftLeft = 0xa0; +constexpr uint64_t kVirtualShiftRight = 0xa1; + +constexpr uint64_t kPhysicalKeyA = 0x00070004; +constexpr uint64_t kPhysicalControlLeft = 0x000700e0; +constexpr uint64_t kPhysicalControlRight = 0x000700e4; +constexpr uint64_t kPhysicalShiftLeft = 0x000700e1; +constexpr uint64_t kPhysicalShiftRight = 0x000700e5; + +constexpr uint64_t kLogicalKeyA = 0x00000061; +constexpr uint64_t kLogicalControlLeft = 0x00300000105; +constexpr uint64_t kLogicalControlRight = 0x00400000105; +constexpr uint64_t kLogicalShiftLeft = 0x0030000010d; +constexpr uint64_t kLogicalShiftRight = 0x0040000010d; } // namespace // Test the most basic key events. @@ -77,8 +94,8 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); - EXPECT_EQ(event->physical, 0x00070004); - EXPECT_EQ(event->logical, 0x00000061); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); EXPECT_STREQ(event->character, "a"); EXPECT_EQ(event->synthesized, false); @@ -95,8 +112,8 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->type, kFlutterKeyEventTypeRepeat); - EXPECT_EQ(event->physical, 0x00070004); - EXPECT_EQ(event->logical, 0x00000061); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); EXPECT_STREQ(event->character, "a"); EXPECT_EQ(event->synthesized, false); @@ -112,8 +129,8 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { EXPECT_EQ(results.size(), 1); event = &results[0]; EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); - EXPECT_EQ(event->physical, 0x00070004); - EXPECT_EQ(event->logical, 0x00000061); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, false); event->callback(false, event->user_data); @@ -189,6 +206,8 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { results.clear(); } +// Key presses that trigger IME should be ignored by this API (and handled by +// compose API). TEST(KeyboardKeyEmbedderHandlerTest, ImeEventsAreIgnored) { std::vector results; TestFlutterKeyEvent* event; @@ -242,5 +261,179 @@ TEST(KeyboardKeyEmbedderHandlerTest, ImeEventsAreIgnored) { EXPECT_EQ(last_handled, true); } +// Test if modifier keys that are told apart by the extended bit +// can be identified. +TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByExtendedBit) { + std::vector results; + TestFlutterKeyEvent* event; + bool last_handled = false; + + std::unique_ptr handler = + std::make_unique( + [&results](const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, void* user_data) { + results.emplace_back(event, callback, user_data); + }); + + // Press Ctrl left. + last_handled = false; + handler->KeyboardHook( + kVirtualControl, kScanCodeControl, WM_KEYDOWN, 0, false, false, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalControlLeft); + EXPECT_EQ(event->logical, kLogicalControlLeft); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); + + // Press Ctrl right. + last_handled = false; + handler->KeyboardHook( + kVirtualControl, kScanCodeControl, WM_KEYDOWN, 0, true, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalControlRight); + EXPECT_EQ(event->logical, kLogicalControlRight); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); + + // Release Ctrl left. + last_handled = false; + handler->KeyboardHook( + kVirtualControl, kScanCodeControl, WM_KEYUP, 0, false, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalControlLeft); + EXPECT_EQ(event->logical, kLogicalControlLeft); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); + + // Release Ctrl right. + last_handled = false; + handler->KeyboardHook( + kVirtualControl, kScanCodeControl, WM_KEYUP, 0, true, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalControlRight); + EXPECT_EQ(event->logical, kLogicalControlRight); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); +} + +// Test if modifier keys that are told apart by the virtual key +// can be identified. +TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByVirtualKey) { + std::vector results; + TestFlutterKeyEvent* event; + bool last_handled = false; + + std::unique_ptr handler = + std::make_unique( + [&results](const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, void* user_data) { + results.emplace_back(event, callback, user_data); + }); + + // Press Shift left. + last_handled = false; + handler->KeyboardHook( + kVirtualShiftLeft, kScanCodeShiftLeft, WM_KEYDOWN, 0, false, false, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalShiftLeft); + EXPECT_EQ(event->logical, kLogicalShiftLeft); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); + + // Press Shift right. + last_handled = false; + handler->KeyboardHook( + kVirtualShiftRight, kScanCodeShiftRight, WM_KEYDOWN, 0, false, false, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalShiftRight); + EXPECT_EQ(event->logical, kLogicalShiftRight); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); + + // Release Shift left. + last_handled = false; + handler->KeyboardHook( + kVirtualShiftLeft, kScanCodeShiftLeft, WM_KEYUP, 0, false, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalShiftLeft); + EXPECT_EQ(event->logical, kLogicalShiftLeft); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); + + // Release Shift right. + last_handled = false; + handler->KeyboardHook( + kVirtualShiftRight, kScanCodeShiftRight, WM_KEYUP, 0, false, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalShiftRight); + EXPECT_EQ(event->logical, kLogicalShiftRight); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); +} + } // namespace testing } // namespace flutter From b5f82a6a2c8b188943f634c66b81129d4ae96e91 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 17 Feb 2021 00:21:26 -0800 Subject: [PATCH 67/82] Key event irregularity --- ...keyboard_key_embedder_handler_unittests.cc | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index 90d141d2d290a..808dc71d3a429 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -435,5 +435,168 @@ TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByVirtualKey) { results.clear(); } +TEST(KeyboardKeyEmbedderHandlerTest, DuplicateDownIsIgnored) { + std::vector results; + TestFlutterKeyEvent* event; + bool last_handled = false; + + std::unique_ptr handler = + std::make_unique( + [&results](const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, void* user_data) { + results.emplace_back(event, callback, user_data); + }); + last_handled = false; + + // Press A + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); + EXPECT_STREQ(event->character, "a"); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); + + // A duplicate A press + last_handled = false; + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, true); + EXPECT_EQ(results.size(), 0); + + // Release A + last_handled = false; + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 'a', false, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); +} + +TEST(KeyboardKeyEmbedderHandlerTest, DuplicateUpIsIgnored) { + std::vector results; + TestFlutterKeyEvent* event; + bool last_handled = false; + + std::unique_ptr handler = + std::make_unique( + [&results](const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, void* user_data) { + results.emplace_back(event, callback, user_data); + }); + last_handled = false; + + // Press A + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); + EXPECT_STREQ(event->character, "a"); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); + + // Release A + last_handled = false; + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 'a', false, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); + + // A duplicate A release + last_handled = false; + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 'a', false, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, true); + EXPECT_EQ(results.size(), 0); +} + +TEST(KeyboardKeyEmbedderHandlerTest, AbruptRepeatIsConvertedtoDown) { + std::vector results; + TestFlutterKeyEvent* event; + bool last_handled = false; + + std::unique_ptr handler = + std::make_unique( + [&results](const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, void* user_data) { + results.emplace_back(event, callback, user_data); + }); + last_handled = false; + + // Press A (with was_down true) + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); + EXPECT_STREQ(event->character, "a"); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); + + // Release A + last_handled = false; + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 'a', false, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); +} + } // namespace testing } // namespace flutter From 64f61a684bbf34f58bb9ad35ed05cb584c9fcdf0 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 17 Feb 2021 01:26:04 -0800 Subject: [PATCH 68/82] Some comments --- shell/platform/windows/flutter_window_winuwp.cc | 8 ++++---- shell/platform/windows/flutter_windows_view.cc | 8 ++++---- shell/platform/windows/keyboard_key_embedder_handler.cc | 5 +++-- shell/platform/windows/keyboard_key_embedder_handler.h | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/shell/platform/windows/flutter_window_winuwp.cc b/shell/platform/windows/flutter_window_winuwp.cc index 82b9dbd6bbefe..a680b369467a3 100644 --- a/shell/platform/windows/flutter_window_winuwp.cc +++ b/shell/platform/windows/flutter_window_winuwp.cc @@ -268,8 +268,8 @@ void FlutterWindowWinUWP::OnKeyUp( int key = static_cast(args.VirtualKey()); char32_t chararacter = static_cast(key | 32); int action = 0x0101; - binding_handler_delegate_->OnKey(key, scancode, action, chararacter, false, - true); + binding_handler_delegate_->OnKey(key, scancode, action, chararacter, false /* extended */, + true /* was_down */); } void FlutterWindowWinUWP::OnKeyDown( @@ -284,8 +284,8 @@ void FlutterWindowWinUWP::OnKeyDown( int key = static_cast(args.VirtualKey()); char32_t chararacter = static_cast(key | 32); int action = 0x0100; - binding_handler_delegate_->OnKey(key, scancode, action, chararacter, false, - false); + binding_handler_delegate_->OnKey(key, scancode, action, chararacter, false /* extended */, + false /* was_down */); } void FlutterWindowWinUWP::OnCharacterReceived( diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 493821a007565..0be988f075b62 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -74,10 +74,10 @@ void FlutterWindowsView::RegisterKeyboardHandlers( // handler that might redispatch events. (See the documentation of // |KeyboardKeyHandler| to learn about redispatching.) // - // This is whether an event is a redispatched event is decided by calculating - // the hash of the event. In order to allow the same real event in the future, - // the handler is "toggled" when events pass through, which means this logic - // does not apply when there are more than 1 handler. + // Whether an event is a redispatched event is decided by calculating the hash + // of the event. In order to allow the same real event in the future, the + // handler is "toggled" when events pass through, which means this logic does + // not apply when there is more than 1 handler. auto key_handler = std::make_unique(SendInput); key_handler->AddDelegate( std::make_unique(messenger)); diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index 4fa2128044f03..e352b3abc96c3 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -178,8 +178,9 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( } } else { // isPhysicalDown is false if (last_logical_record == 0) { - // The physical key has been released before. It indicates multiple - // keyboards pressed keys with the same physical key. Ignore the up event. + // The physical key has been released before. It might indicate a missed + // event due to loss of focus, or multiple keyboards pressed keys with the + // same physical key. Ignore the up event. callback(true); return; } else { diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index 8f6020c2384aa..1ea2d9d6a3199 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -18,7 +18,7 @@ namespace flutter { namespace { // An arbitrary size for the character cache in bytes. // -// It should hold a UTF-32 character encoded in UTF-8 as long as the trailing +// It should hold a UTF-32 character encoded in UTF-8 as well as the trailing // '\0'. constexpr size_t kCharacterCacheSize = 8; } // namespace From 4e759d02cef99f9e8fb877e3e7b6976cd8cc99c0 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 17 Feb 2021 01:27:13 -0800 Subject: [PATCH 69/82] Comments --- shell/platform/windows/keyboard_key_handler.cc | 2 +- shell/platform/windows/keyboard_key_handler.h | 4 ++-- shell/platform/windows/keyboard_key_handler_unittests.cc | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index 7f979197c6367..e2c5dbc25f70d 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -52,7 +52,7 @@ void KeyboardKeyHandler::AddDelegate( delegates_.push_back(std::move(delegate)); } -size_t KeyboardKeyHandler::RedispatchedAmount() { +size_t KeyboardKeyHandler::RedispatchedCount() { return pending_redispatches_.size(); } diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index 1163c74ddee20..9ca319659041b 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -103,7 +103,7 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { void ComposeChangeHook(const std::u16string& text, int cursor_pos) override; protected: - size_t RedispatchedAmount(); + size_t RedispatchedCount(); private: struct PendingEvent { @@ -161,7 +161,7 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { // // Another alternative would be to compute a checksum from all the data in the // event (just compute it over the bytes in the struct, probably skipping - // timestamps), but the fields used below are enough to differentiate them, and + // timestamps), but the fields used are enough to differentiate them, and // since Windows does some processing on the events (coming up with virtual key // codes, setting timestamps, etc.), it's not clear that the redispatched // events would have the same checksums. diff --git a/shell/platform/windows/keyboard_key_handler_unittests.cc b/shell/platform/windows/keyboard_key_handler_unittests.cc index 88e79d07cd118..13cce1fa2fda8 100644 --- a/shell/platform/windows/keyboard_key_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_handler_unittests.cc @@ -90,7 +90,7 @@ class TestKeyboardKeyHandler : public KeyboardKeyHandler { : KeyboardKeyHandler(redispatch_event) {} bool HasRedispatched() { - return RedispatchedAmount() > 0; + return RedispatchedCount() > 0; } }; From 9156bac0ecc74c28a5560e79e6c58302aceb206e Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 17 Feb 2021 02:23:12 -0800 Subject: [PATCH 70/82] Privatize string convertsion --- .../windows/keyboard_key_embedder_handler.cc | 39 ++++++++++++------- .../windows/keyboard_key_embedder_handler.h | 11 ++---- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index e352b3abc96c3..1c8820b03ad6f 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -16,7 +16,12 @@ namespace flutter { -namespace {} // namespace +namespace { +// An arbitrary size for the character cache in bytes. +// +// It should hold a UTF-32 character encoded in UTF-8 as well as the trailing +// '\0'. +constexpr size_t kCharacterCacheSize = 8; /** * The code prefix for keys which do not have a Unicode representation. @@ -45,6 +50,8 @@ constexpr uint64_t kWindowsKeyIdPlane = 0x00700000000; */ constexpr uint64_t kAutogeneratedMask = 0x10000000000; +} // namespace + KeyboardKeyEmbedderHandler::KeyboardKeyEmbedderHandler( std::function(character)}); - strcpy_s(character_cache_, kCharacterCacheSize, Utf8FromUtf16(text).c_str()); -} - void KeyboardKeyEmbedderHandler::KeyboardHook( int key, int scancode, @@ -150,6 +147,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( // The next value of pressingRecords_[physical_key] (or to remove it). uint64_t next_logical_record; bool next_has_record = true; + char character_bytes[kCharacterCacheSize]; if (is_physical_down) { if (had_record) { @@ -157,7 +155,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( // A normal repeated key. type = kFlutterKeyEventTypeRepeat; assert(had_record); - CacheUtf8String(character); + ConvertUtf32ToUtf8_(character_bytes, character); next_logical_record = last_logical_record; result_logical_key = last_logical_record; } else { @@ -172,7 +170,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( // A normal down event (whether the system event is a repeat or not). type = kFlutterKeyEventTypeDown; assert(!had_record); - CacheUtf8String(character); + ConvertUtf32ToUtf8_(character_bytes, character); next_logical_record = logical_key; result_logical_key = logical_key; } @@ -188,7 +186,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( type = kFlutterKeyEventTypeUp; assert(had_record); // Up events never have character. - character_cache_[0] = '\0'; + character_bytes[0] = '\0'; next_has_record = false; result_logical_key = last_logical_record; } @@ -218,7 +216,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( key_data.type = type; key_data.physical = physical_key; key_data.logical = result_logical_key; - key_data.character = character_cache_; + key_data.character = character_bytes; key_data.synthesized = false; response_id_ += 1; @@ -247,4 +245,15 @@ void KeyboardKeyEmbedderHandler::HandleResponse(bool handled, void* user_data) { callback(handled, pending->response_id); } +void KeyboardKeyEmbedderHandler::ConvertUtf32ToUtf8_(char* out, char32_t ch) { + if (ch == 0) { + out[0] = '\0'; + return; + } + // TODO: Correctly handle UTF-32 + std::wstring text({static_cast(ch)}); + strcpy_s(out, kCharacterCacheSize, Utf8FromUtf16(text).c_str()); +} + + } // namespace flutter diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index 1ea2d9d6a3199..a9cfd7ac13d44 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -16,11 +16,6 @@ namespace flutter { namespace { -// An arbitrary size for the character cache in bytes. -// -// It should hold a UTF-32 character encoded in UTF-8 as well as the trailing -// '\0'. -constexpr size_t kCharacterCacheSize = 8; } // namespace // A delegate of |KeyboardKeyHandler| that handles events by sending @@ -60,15 +55,17 @@ class KeyboardKeyEmbedderHandler void CacheUtf8String(char32_t ch); + // A map from physical keys to logical keys, each entry indicating a pressed key. + std::map pressingRecords_; std::function sendEvent_; - std::map pressingRecords_; - char character_cache_[kCharacterCacheSize]; std::map> pending_responses_; uint64_t response_id_; static uint64_t getPhysicalKey(int scancode, bool extended); static uint64_t getLogicalKey(int key, bool extended, int scancode); + static void HandleResponse(bool handled, void* user_data); + static void ConvertUtf32ToUtf8_(char* out, char32_t ch); static std::map windowsToPhysicalMap_; static std::map windowsToLogicalMap_; From aca9f8f0118d62321d1d56f6ac46269aa7419a86 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 17 Feb 2021 04:19:24 -0800 Subject: [PATCH 71/82] Impl check keys --- .../windows/keyboard_key_embedder_handler.cc | 139 ++++++++++++++++-- .../windows/keyboard_key_embedder_handler.h | 30 +++- 2 files changed, 156 insertions(+), 13 deletions(-) diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index 1c8820b03ad6f..f2a044a4db069 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -50,13 +50,28 @@ constexpr uint64_t kWindowsKeyIdPlane = 0x00700000000; */ constexpr uint64_t kAutogeneratedMask = 0x10000000000; +constexpr SHORT kStateMaskToggled = 0x01; +constexpr SHORT kStateMaskPressed = 0x80; + } // namespace +KeyboardKeyEmbedderHandler::CheckedKey::CheckedKey(UINT virtual_key, bool extended, bool check_pressed, bool check_toggled) : + check_pressed(check_pressed), check_toggled(check_toggled) { + UINT scan_code = MapVirtualKey(virtual_key, MAPVK_VK_TO_VSC); + physical_key = getPhysicalKey(scan_code, extended); + logical_key = getLogicalKey(virtual_key, extended, scan_code); + if (check_toggled) { + toggled_on = GetKeyState(virtual_key) & kStateMaskToggled; + } +} + KeyboardKeyEmbedderHandler::KeyboardKeyEmbedderHandler( std::function send_event) - : sendEvent_(send_event), response_id_(1) {} + : sendEvent_(send_event), response_id_(1) { + InitCheckedKeys(); +} KeyboardKeyEmbedderHandler::~KeyboardKeyEmbedderHandler() = default; @@ -207,17 +222,22 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( return; } - FlutterKeyEvent key_data = {}; - key_data.struct_size = sizeof(FlutterKeyEvent); - key_data.timestamp = + UpdateLastSeenCritialKey(key, physical_key, result_logical_key); + SynchroizeCritialKeys(type == kFlutterKeyEventTypeDown ? key : 0); + + FlutterKeyEvent key_data { + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = + static_cast( std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch()) - .count(); - key_data.type = type; - key_data.physical = physical_key; - key_data.logical = result_logical_key; - key_data.character = character_bytes; - key_data.synthesized = false; + .count()), + .type = type, + .physical = physical_key, + .logical = result_logical_key, + .character = character_bytes, + .synthesized = false, + }; response_id_ += 1; uint64_t response_id = response_id_; @@ -239,12 +259,95 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( reinterpret_cast(pending_responses_[response_id].get())); } +void KeyboardKeyEmbedderHandler::UpdateLastSeenCritialKey(int virtual_key, uint64_t physical_key, uint64_t logical_key) { + auto found = critical_keys_.find(virtual_key); + if (found != critical_keys_.end()) { + found->second.physical_key = physical_key; + found->second.logical_key = logical_key; + } +} + +void KeyboardKeyEmbedderHandler::SynchroizeCritialKeys(int toggle_virtual_key) { + for (auto& kv : critical_keys_) { + UINT virtual_key = kv.first; + CheckedKey& key_info = kv.second; + if (key_info.physical_key == 0) { + // Never seen this key. + continue; + } + assert(key_info.logical_key != 0); + SHORT state = GetKeyState(virtual_key); + + // Check toggling state first, because it might alter pressing state. + if (key_info.check_toggled) { + bool should_toggled = state & kStateMaskToggled; + if (virtual_key == toggle_virtual_key) { + printf("State %04x\n", state); + key_info.toggled_on = !key_info.toggled_on; + } + if (key_info.toggled_on != should_toggled) { + const char* empty_character = ""; + // If the key is pressed, release it first. + if (pressingRecords_.find(key_info.physical_key) != pressingRecords_.end()) { + sendEvent_(SynthesizeSimpleEvent( + kFlutterKeyEventTypeUp, + key_info.physical_key, + key_info.logical_key, + empty_character), + nullptr, nullptr); + } else { + // This key will be pressed in the following synthesized event. + pressingRecords_[key_info.physical_key] = key_info.logical_key; + } + sendEvent_(SynthesizeSimpleEvent( + kFlutterKeyEventTypeDown, + key_info.physical_key, + key_info.logical_key, + empty_character), + nullptr, nullptr); + } + key_info.toggled_on = should_toggled; + } + + if (key_info.check_pressed) { + auto recorded_pressed_iter = pressingRecords_.find(key_info.physical_key); + bool recorded_pressed = recorded_pressed_iter != pressingRecords_.end(); + bool should_pressed = state & kStateMaskPressed; + if (recorded_pressed != should_pressed) { + if (should_pressed) { + pressingRecords_[key_info.physical_key] = key_info.logical_key; + } else { + pressingRecords_.erase(recorded_pressed_iter); + } + const char* empty_character = ""; + sendEvent_(SynthesizeSimpleEvent( + should_pressed ? kFlutterKeyEventTypeDown : kFlutterKeyEventTypeUp, + key_info.physical_key, + key_info.logical_key, + empty_character), + nullptr, nullptr); + } + } + } +} + void KeyboardKeyEmbedderHandler::HandleResponse(bool handled, void* user_data) { PendingResponse* pending = reinterpret_cast(user_data); auto callback = std::move(pending->callback); callback(handled, pending->response_id); } +void KeyboardKeyEmbedderHandler::InitCheckedKeys() { + critical_keys_.emplace(VK_LSHIFT, CheckedKey(VK_LSHIFT, false, true, false)); + critical_keys_.emplace(VK_RSHIFT, CheckedKey(VK_RSHIFT, false, true, false)); + critical_keys_.emplace(VK_LCONTROL, CheckedKey(VK_LCONTROL, false, true, false)); + critical_keys_.emplace(VK_RCONTROL, CheckedKey(VK_RCONTROL, true, true, false)); + + critical_keys_.emplace(VK_CAPITAL, CheckedKey(VK_CAPITAL, false, true, true)); + critical_keys_.emplace(VK_SCROLL, CheckedKey(VK_SCROLL, false, true, true)); + critical_keys_.emplace(VK_NUMLOCK, CheckedKey(VK_NUMLOCK, true, true, true)); +} + void KeyboardKeyEmbedderHandler::ConvertUtf32ToUtf8_(char* out, char32_t ch) { if (ch == 0) { out[0] = '\0'; @@ -255,5 +358,21 @@ void KeyboardKeyEmbedderHandler::ConvertUtf32ToUtf8_(char* out, char32_t ch) { strcpy_s(out, kCharacterCacheSize, Utf8FromUtf16(text).c_str()); } +FlutterKeyEvent KeyboardKeyEmbedderHandler::SynthesizeSimpleEvent(FlutterKeyEventType type, uint64_t physical, uint64_t logical, const char* character) { + return FlutterKeyEvent { + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = + static_cast( + std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count()), + .type = type, + .physical = physical, + .logical = logical, + .character = character, + .synthesized = true, + }; +} + } // namespace flutter diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index a9cfd7ac13d44..a87584879d976 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -53,7 +53,25 @@ class KeyboardKeyEmbedderHandler uint64_t response_id; }; - void CacheUtf8String(char32_t ch); + // Record the last seen physical and logical key for a virtual key. + struct CheckedKey { + CheckedKey(UINT virtual_key, bool extended, bool check_pressed, bool check_toggled); + + // Last seen value of physical key and logical key for the virtual key. + uint64_t physical_key; + uint64_t logical_key; + + // Whether to ensure the pressing state of the key (usually for modifier keys). + bool check_pressed; + // Whether to ensure the toggled state of the key (usually for lock keys). + bool check_toggled; + // Whether the lock key is currently toggled on. + bool toggled_on; + }; + + void InitCheckedKeys(); + void UpdateLastSeenCritialKey(int virtual_key, uint64_t physical_key, uint64_t logical_key); + void SynchroizeCritialKeys(int this_virtual_key); // A map from physical keys to logical keys, each entry indicating a pressed key. std::map pressingRecords_; @@ -62,16 +80,22 @@ class KeyboardKeyEmbedderHandler std::map> pending_responses_; uint64_t response_id_; + // Important keys whose states are checked and guaranteed synchronized + // on every key event. + // + // The following maps map Win32 virtual key to the physical key and logical + // key they're last seen. + std::map critical_keys_; + static uint64_t getPhysicalKey(int scancode, bool extended); static uint64_t getLogicalKey(int key, bool extended, int scancode); static void HandleResponse(bool handled, void* user_data); static void ConvertUtf32ToUtf8_(char* out, char32_t ch); + static FlutterKeyEvent SynthesizeSimpleEvent(FlutterKeyEventType type, uint64_t physical, uint64_t logical, const char* character); static std::map windowsToPhysicalMap_; static std::map windowsToLogicalMap_; static std::map scanCodeToLogicalMap_; - - static void HandleResponse(bool handled, void* user_data); }; } // namespace flutter From 108f56c78d2f271b9c5b118be3262bf1fe9ddbe7 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 17 Feb 2021 04:55:11 -0800 Subject: [PATCH 72/82] Adapt unittests --- .../platform/windows/flutter_windows_view.cc | 3 +- .../windows/keyboard_key_embedder_handler.cc | 45 +-- .../windows/keyboard_key_embedder_handler.h | 11 +- ...keyboard_key_embedder_handler_unittests.cc | 320 ++++++++++-------- 4 files changed, 211 insertions(+), 168 deletions(-) diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 0be988f075b62..4f108f7f63b2b 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -85,7 +85,8 @@ void FlutterWindowsView::RegisterKeyboardHandlers( [this](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* user_data) { return engine_->SendKeyEvent(event, callback, user_data); - })); + }, + GetKeyState)); AddKeyboardHandler(std::move(key_handler)); AddKeyboardHandler( std::make_unique(messenger, this)); diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index f2a044a4db069..ca8de61d824a8 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -55,21 +55,13 @@ constexpr SHORT kStateMaskPressed = 0x80; } // namespace -KeyboardKeyEmbedderHandler::CheckedKey::CheckedKey(UINT virtual_key, bool extended, bool check_pressed, bool check_toggled) : - check_pressed(check_pressed), check_toggled(check_toggled) { - UINT scan_code = MapVirtualKey(virtual_key, MAPVK_VK_TO_VSC); - physical_key = getPhysicalKey(scan_code, extended); - logical_key = getLogicalKey(virtual_key, extended, scan_code); - if (check_toggled) { - toggled_on = GetKeyState(virtual_key) & kStateMaskToggled; - } -} - KeyboardKeyEmbedderHandler::KeyboardKeyEmbedderHandler( std::function send_event) - : sendEvent_(send_event), response_id_(1) { + void* user_data)> send_event, + GetKeyStateHandler get_key_state + ) + : sendEvent_(send_event), get_key_state_(get_key_state), response_id_(1) { InitCheckedKeys(); } @@ -276,7 +268,7 @@ void KeyboardKeyEmbedderHandler::SynchroizeCritialKeys(int toggle_virtual_key) { continue; } assert(key_info.logical_key != 0); - SHORT state = GetKeyState(virtual_key); + SHORT state = get_key_state_(virtual_key); // Check toggling state first, because it might alter pressing state. if (key_info.check_toggled) { @@ -338,14 +330,25 @@ void KeyboardKeyEmbedderHandler::HandleResponse(bool handled, void* user_data) { } void KeyboardKeyEmbedderHandler::InitCheckedKeys() { - critical_keys_.emplace(VK_LSHIFT, CheckedKey(VK_LSHIFT, false, true, false)); - critical_keys_.emplace(VK_RSHIFT, CheckedKey(VK_RSHIFT, false, true, false)); - critical_keys_.emplace(VK_LCONTROL, CheckedKey(VK_LCONTROL, false, true, false)); - critical_keys_.emplace(VK_RCONTROL, CheckedKey(VK_RCONTROL, true, true, false)); - - critical_keys_.emplace(VK_CAPITAL, CheckedKey(VK_CAPITAL, false, true, true)); - critical_keys_.emplace(VK_SCROLL, CheckedKey(VK_SCROLL, false, true, true)); - critical_keys_.emplace(VK_NUMLOCK, CheckedKey(VK_NUMLOCK, true, true, true)); + auto createCheckedKey = [this](UINT virtual_key, bool extended, bool check_pressed, bool check_toggled) -> CheckedKey { + UINT scan_code = MapVirtualKey(virtual_key, MAPVK_VK_TO_VSC); + return CheckedKey{ + .physical_key = getPhysicalKey(scan_code, extended), + .logical_key = getLogicalKey(virtual_key, extended, scan_code), + .check_pressed = check_pressed, + .check_toggled = check_toggled, + .toggled_on = check_toggled ? !!(get_key_state_(virtual_key) & kStateMaskToggled) : false, + }; + }; + + critical_keys_.emplace(VK_LSHIFT, createCheckedKey(VK_LSHIFT, false, true, false)); + critical_keys_.emplace(VK_RSHIFT, createCheckedKey(VK_RSHIFT, false, true, false)); + critical_keys_.emplace(VK_LCONTROL, createCheckedKey(VK_LCONTROL, false, true, false)); + critical_keys_.emplace(VK_RCONTROL, createCheckedKey(VK_RCONTROL, true, true, false)); + + critical_keys_.emplace(VK_CAPITAL, createCheckedKey(VK_CAPITAL, false, true, true)); + critical_keys_.emplace(VK_SCROLL, createCheckedKey(VK_SCROLL, false, true, true)); + critical_keys_.emplace(VK_NUMLOCK, createCheckedKey(VK_NUMLOCK, true, true, true)); } void KeyboardKeyEmbedderHandler::ConvertUtf32ToUtf8_(char* out, char32_t ch) { diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index a87584879d976..1d7806810589f 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -28,13 +28,14 @@ class KeyboardKeyEmbedderHandler using SendEvent = std::function; + using GetKeyStateHandler = std::function; // Build a KeyboardKeyEmbedderHandler. // // Use `send_event` to define how the manager should dispatch converted // flutter events, as well as how to receive the resopnse, to the engine. It's // typically FlutterWindowsEngine::SendKeyEvent. - explicit KeyboardKeyEmbedderHandler(SendEvent send_event); + explicit KeyboardKeyEmbedderHandler(SendEvent send_event, GetKeyStateHandler get_key_state); virtual ~KeyboardKeyEmbedderHandler(); @@ -55,8 +56,6 @@ class KeyboardKeyEmbedderHandler // Record the last seen physical and logical key for a virtual key. struct CheckedKey { - CheckedKey(UINT virtual_key, bool extended, bool check_pressed, bool check_toggled); - // Last seen value of physical key and logical key for the virtual key. uint64_t physical_key; uint64_t logical_key; @@ -73,10 +72,12 @@ class KeyboardKeyEmbedderHandler void UpdateLastSeenCritialKey(int virtual_key, uint64_t physical_key, uint64_t logical_key); void SynchroizeCritialKeys(int this_virtual_key); - // A map from physical keys to logical keys, each entry indicating a pressed key. - std::map pressingRecords_; std::function sendEvent_; + GetKeyStateHandler get_key_state_; + + // A map from physical keys to logical keys, each entry indicating a pressed key. + std::map pressingRecords_; std::map> pending_responses_; uint64_t response_id_; diff --git a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index 808dc71d3a429..f5137d1b88cde 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -15,6 +15,9 @@ namespace flutter { namespace { +constexpr SHORT kStateMaskToggled = 0x01; +constexpr SHORT kStateMaskPressed = 0x80; + class TestFlutterKeyEvent : public FlutterKeyEvent { public: TestFlutterKeyEvent(const FlutterKeyEvent& src, @@ -37,6 +40,24 @@ class TestFlutterKeyEvent : public FlutterKeyEvent { const std::string character_str; }; +class TestKeystate { + public: + void Set(int virtual_key, bool pressed, bool toggled_on = false) { + state_[virtual_key] = (pressed ? kStateMaskPressed : 0) | (toggled_on ? kStateMaskToggled : 0); + } + + SHORT Get(int virtual_key) { + return state_[virtual_key]; + } + + KeyboardKeyEmbedderHandler::GetKeyStateHandler Getter() { + return [this](int virtual_key) {return Get(virtual_key);}; + } + + private: + std::map state_; +}; + } // namespace namespace testing { @@ -49,14 +70,7 @@ constexpr uint64_t kScanCodeControl = 0x1d; constexpr uint64_t kScanCodeShiftLeft = 0x2a; constexpr uint64_t kScanCodeShiftRight = 0x36; -constexpr uint64_t kVirtualProcessKey = 0xe5; constexpr uint64_t kVirtualKeyA = 0x41; -constexpr uint64_t kVirtualNumpad1 = 0x61; -constexpr uint64_t kVirtualNumpadEnd = 0x23; -constexpr uint64_t kVirtualNumLock = 0x90; -constexpr uint64_t kVirtualControl = 0xa2; -constexpr uint64_t kVirtualShiftLeft = 0xa0; -constexpr uint64_t kVirtualShiftRight = 0xa1; constexpr uint64_t kPhysicalKeyA = 0x00070004; constexpr uint64_t kPhysicalControlLeft = 0x000700e0; @@ -75,6 +89,7 @@ constexpr uint64_t kLogicalShiftRight = 0x0040000010d; // // Press, hold, and release key A on an US keyboard. TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { + TestKeystate key_state; std::vector results; TestFlutterKeyEvent* event; bool last_handled = false; @@ -84,7 +99,8 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { [&results](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); - }); + }, + key_state.Getter()); // Press KeyA. handler->KeyboardHook( @@ -101,8 +117,8 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { event->callback(true, event->user_data); EXPECT_EQ(last_handled, true); - results.clear(); + key_state.Set(kVirtualKeyA, true); // Hold KeyA. handler->KeyboardHook( @@ -119,7 +135,6 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { event->callback(false, event->user_data); EXPECT_EQ(last_handled, false); - results.clear(); // Release KeyA. @@ -142,6 +157,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { // This is special because the virtual key for numpad 1 will // change in this process. TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { + TestKeystate key_state; std::vector results; TestFlutterKeyEvent* event; bool last_handled = false; @@ -151,11 +167,12 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { [&results](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); - }); + }, key_state.Getter()); // Press NumPad1. + key_state.Set(VK_NUMPAD1, true); handler->KeyboardHook( - kVirtualNumpad1, kScanCodeNumpad1, WM_KEYDOWN, 0, false, false, + VK_NUMPAD1, kScanCodeNumpad1, WM_KEYDOWN, 0, false, false, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; @@ -167,8 +184,9 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { results.clear(); // Press NumLock. + key_state.Set(VK_NUMLOCK, true, true); handler->KeyboardHook( - kVirtualNumLock, kScanCodeNumLock, WM_KEYDOWN, 0, true, false, + VK_NUMLOCK, kScanCodeNumLock, WM_KEYDOWN, 0, true, false, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; @@ -180,8 +198,9 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { results.clear(); // Release NumLock. + key_state.Set(VK_NUMLOCK, false, true); handler->KeyboardHook( - kVirtualNumLock, kScanCodeNumLock, WM_KEYUP, 0, true, true, + VK_NUMLOCK, kScanCodeNumLock, WM_KEYUP, 0, true, true, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; @@ -194,7 +213,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { // Release NumPad1. (The logical key is now NumpadEnd) handler->KeyboardHook( - kVirtualNumpadEnd, kScanCodeNumpad1, WM_KEYUP, 0, false, true, + VK_END, kScanCodeNumpad1, WM_KEYUP, 0, false, true, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(results.size(), 1); event = &results[0]; @@ -209,6 +228,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { // Key presses that trigger IME should be ignored by this API (and handled by // compose API). TEST(KeyboardKeyEmbedderHandlerTest, ImeEventsAreIgnored) { + TestKeystate key_state; std::vector results; TestFlutterKeyEvent* event; bool last_handled = false; @@ -218,12 +238,12 @@ TEST(KeyboardKeyEmbedderHandlerTest, ImeEventsAreIgnored) { [&results](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); - }); + }, key_state.Getter()); // Press A in an IME last_handled = false; handler->KeyboardHook( - kVirtualProcessKey, kScanCodeKeyA, WM_KEYDOWN, 0, true, false, + VK_PROCESSKEY, kScanCodeKeyA, WM_KEYDOWN, 0, true, false, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, true); @@ -238,6 +258,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, ImeEventsAreIgnored) { EXPECT_EQ(results.size(), 0); // Press A out of an IME + key_state.Set(kVirtualKeyA, true); last_handled = false; handler->KeyboardHook( kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 0, true, false, @@ -251,6 +272,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, ImeEventsAreIgnored) { results.clear(); last_handled = false; + key_state.Set(kVirtualKeyA, false); handler->KeyboardHook( kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 0, true, false, [&last_handled](bool handled) { last_handled = handled; }); @@ -264,6 +286,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, ImeEventsAreIgnored) { // Test if modifier keys that are told apart by the extended bit // can be identified. TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByExtendedBit) { + TestKeystate key_state; std::vector results; TestFlutterKeyEvent* event; bool last_handled = false; @@ -273,12 +296,13 @@ TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByExtendedBit) { [&results](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); - }); + }, key_state.Getter()); // Press Ctrl left. last_handled = false; + key_state.Set(VK_LCONTROL, true); handler->KeyboardHook( - kVirtualControl, kScanCodeControl, WM_KEYDOWN, 0, false, false, + VK_CONTROL, kScanCodeControl, WM_KEYDOWN, 0, false, false, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, false); EXPECT_EQ(results.size(), 1); @@ -295,8 +319,9 @@ TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByExtendedBit) { // Press Ctrl right. last_handled = false; + key_state.Set(VK_RCONTROL, true); handler->KeyboardHook( - kVirtualControl, kScanCodeControl, WM_KEYDOWN, 0, true, true, + VK_CONTROL, kScanCodeControl, WM_KEYDOWN, 0, true, true, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, false); EXPECT_EQ(results.size(), 1); @@ -313,8 +338,9 @@ TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByExtendedBit) { // Release Ctrl left. last_handled = false; + key_state.Set(VK_LCONTROL, false); handler->KeyboardHook( - kVirtualControl, kScanCodeControl, WM_KEYUP, 0, false, true, + VK_CONTROL, kScanCodeControl, WM_KEYUP, 0, false, true, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, false); EXPECT_EQ(results.size(), 1); @@ -331,8 +357,9 @@ TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByExtendedBit) { // Release Ctrl right. last_handled = false; + key_state.Set(VK_RCONTROL, false); handler->KeyboardHook( - kVirtualControl, kScanCodeControl, WM_KEYUP, 0, true, true, + VK_CONTROL, kScanCodeControl, WM_KEYUP, 0, true, true, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, false); EXPECT_EQ(results.size(), 1); @@ -351,6 +378,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByExtendedBit) { // Test if modifier keys that are told apart by the virtual key // can be identified. TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByVirtualKey) { + TestKeystate key_state; std::vector results; TestFlutterKeyEvent* event; bool last_handled = false; @@ -360,12 +388,13 @@ TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByVirtualKey) { [&results](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); - }); + }, key_state.Getter()); // Press Shift left. last_handled = false; + key_state.Set(VK_LSHIFT, true); handler->KeyboardHook( - kVirtualShiftLeft, kScanCodeShiftLeft, WM_KEYDOWN, 0, false, false, + VK_LSHIFT, kScanCodeShiftLeft, WM_KEYDOWN, 0, false, false, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, false); EXPECT_EQ(results.size(), 1); @@ -382,8 +411,9 @@ TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByVirtualKey) { // Press Shift right. last_handled = false; + key_state.Set(VK_RSHIFT, true); handler->KeyboardHook( - kVirtualShiftRight, kScanCodeShiftRight, WM_KEYDOWN, 0, false, false, + VK_RSHIFT, kScanCodeShiftRight, WM_KEYDOWN, 0, false, false, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, false); EXPECT_EQ(results.size(), 1); @@ -400,8 +430,9 @@ TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByVirtualKey) { // Release Shift left. last_handled = false; + key_state.Set(VK_LSHIFT, false); handler->KeyboardHook( - kVirtualShiftLeft, kScanCodeShiftLeft, WM_KEYUP, 0, false, true, + VK_LSHIFT, kScanCodeShiftLeft, WM_KEYUP, 0, false, true, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, false); EXPECT_EQ(results.size(), 1); @@ -418,8 +449,9 @@ TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByVirtualKey) { // Release Shift right. last_handled = false; + key_state.Set(VK_RSHIFT, false); handler->KeyboardHook( - kVirtualShiftRight, kScanCodeShiftRight, WM_KEYUP, 0, false, true, + VK_RSHIFT, kScanCodeShiftRight, WM_KEYUP, 0, false, true, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, false); EXPECT_EQ(results.size(), 1); @@ -435,121 +467,124 @@ TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByVirtualKey) { results.clear(); } -TEST(KeyboardKeyEmbedderHandlerTest, DuplicateDownIsIgnored) { - std::vector results; - TestFlutterKeyEvent* event; - bool last_handled = false; - - std::unique_ptr handler = - std::make_unique( - [&results](const FlutterKeyEvent& event, - FlutterKeyEventCallback callback, void* user_data) { - results.emplace_back(event, callback, user_data); - }); - last_handled = false; - - // Press A - handler->KeyboardHook( - kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, - [&last_handled](bool handled) { last_handled = handled; }); - EXPECT_EQ(last_handled, false); - EXPECT_EQ(results.size(), 1); - event = &results[0]; - EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); - EXPECT_EQ(event->physical, kPhysicalKeyA); - EXPECT_EQ(event->logical, kLogicalKeyA); - EXPECT_STREQ(event->character, "a"); - EXPECT_EQ(event->synthesized, false); - - event->callback(true, event->user_data); - EXPECT_EQ(last_handled, true); - results.clear(); - - // A duplicate A press - last_handled = false; - handler->KeyboardHook( - kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, - [&last_handled](bool handled) { last_handled = handled; }); - EXPECT_EQ(last_handled, true); - EXPECT_EQ(results.size(), 0); - - // Release A - last_handled = false; - handler->KeyboardHook( - kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 'a', false, true, - [&last_handled](bool handled) { last_handled = handled; }); - EXPECT_EQ(last_handled, false); - EXPECT_EQ(results.size(), 1); - event = &results[0]; - EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); - EXPECT_EQ(event->physical, kPhysicalKeyA); - EXPECT_EQ(event->logical, kLogicalKeyA); - EXPECT_STREQ(event->character, ""); - EXPECT_EQ(event->synthesized, false); - - event->callback(true, event->user_data); - EXPECT_EQ(last_handled, true); - results.clear(); -} - -TEST(KeyboardKeyEmbedderHandlerTest, DuplicateUpIsIgnored) { - std::vector results; - TestFlutterKeyEvent* event; - bool last_handled = false; - - std::unique_ptr handler = - std::make_unique( - [&results](const FlutterKeyEvent& event, - FlutterKeyEventCallback callback, void* user_data) { - results.emplace_back(event, callback, user_data); - }); - last_handled = false; - - // Press A - handler->KeyboardHook( - kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, - [&last_handled](bool handled) { last_handled = handled; }); - EXPECT_EQ(last_handled, false); - EXPECT_EQ(results.size(), 1); - event = &results[0]; - EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); - EXPECT_EQ(event->physical, kPhysicalKeyA); - EXPECT_EQ(event->logical, kLogicalKeyA); - EXPECT_STREQ(event->character, "a"); - EXPECT_EQ(event->synthesized, false); - - event->callback(true, event->user_data); - EXPECT_EQ(last_handled, true); - results.clear(); - - // Release A - last_handled = false; - handler->KeyboardHook( - kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 'a', false, true, - [&last_handled](bool handled) { last_handled = handled; }); - EXPECT_EQ(last_handled, false); - EXPECT_EQ(results.size(), 1); - event = &results[0]; - EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); - EXPECT_EQ(event->physical, kPhysicalKeyA); - EXPECT_EQ(event->logical, kLogicalKeyA); - EXPECT_STREQ(event->character, ""); - EXPECT_EQ(event->synthesized, false); - - event->callback(true, event->user_data); - EXPECT_EQ(last_handled, true); - results.clear(); - - // A duplicate A release - last_handled = false; - handler->KeyboardHook( - kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 'a', false, true, - [&last_handled](bool handled) { last_handled = handled; }); - EXPECT_EQ(last_handled, true); - EXPECT_EQ(results.size(), 0); -} +// TEST(KeyboardKeyEmbedderHandlerTest, DuplicateDownIsIgnored) { +// TestKeystate key_state; +// std::vector results; +// TestFlutterKeyEvent* event; +// bool last_handled = false; + +// std::unique_ptr handler = +// std::make_unique( +// [&results](const FlutterKeyEvent& event, +// FlutterKeyEventCallback callback, void* user_data) { +// results.emplace_back(event, callback, user_data); +// }, key_state.Getter()); + +// // Press A +// last_handled = false; +// handler->KeyboardHook( +// kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, +// [&last_handled](bool handled) { last_handled = handled; }); +// EXPECT_EQ(last_handled, false); +// EXPECT_EQ(results.size(), 1); +// event = &results[0]; +// EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); +// EXPECT_EQ(event->physical, kPhysicalKeyA); +// EXPECT_EQ(event->logical, kLogicalKeyA); +// EXPECT_STREQ(event->character, "a"); +// EXPECT_EQ(event->synthesized, false); + +// event->callback(true, event->user_data); +// EXPECT_EQ(last_handled, true); +// results.clear(); +// key_state.Set(kVirtualKeyA, true); + +// // A duplicate A press +// last_handled = false; +// handler->KeyboardHook( +// kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, +// [&last_handled](bool handled) { last_handled = handled; }); +// EXPECT_EQ(last_handled, true); +// EXPECT_EQ(results.size(), 0); + +// // Release A +// last_handled = false; +// handler->KeyboardHook( +// kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 'a', false, true, +// [&last_handled](bool handled) { last_handled = handled; }); +// EXPECT_EQ(last_handled, false); +// EXPECT_EQ(results.size(), 1); +// event = &results[0]; +// EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); +// EXPECT_EQ(event->physical, kPhysicalKeyA); +// EXPECT_EQ(event->logical, kLogicalKeyA); +// EXPECT_STREQ(event->character, ""); +// EXPECT_EQ(event->synthesized, false); + +// event->callback(true, event->user_data); +// EXPECT_EQ(last_handled, true); +// results.clear(); +// } + +// TEST(KeyboardKeyEmbedderHandlerTest, DuplicateUpIsIgnored) { +// std::vector results; +// TestFlutterKeyEvent* event; +// bool last_handled = false; + +// std::unique_ptr handler = +// std::make_unique( +// [&results](const FlutterKeyEvent& event, +// FlutterKeyEventCallback callback, void* user_data) { +// results.emplace_back(event, callback, user_data); +// }); +// last_handled = false; + +// // Press A +// handler->KeyboardHook( +// kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, +// [&last_handled](bool handled) { last_handled = handled; }); +// EXPECT_EQ(last_handled, false); +// EXPECT_EQ(results.size(), 1); +// event = &results[0]; +// EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); +// EXPECT_EQ(event->physical, kPhysicalKeyA); +// EXPECT_EQ(event->logical, kLogicalKeyA); +// EXPECT_STREQ(event->character, "a"); +// EXPECT_EQ(event->synthesized, false); + +// event->callback(true, event->user_data); +// EXPECT_EQ(last_handled, true); +// results.clear(); + +// // Release A +// last_handled = false; +// handler->KeyboardHook( +// kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 'a', false, true, +// [&last_handled](bool handled) { last_handled = handled; }); +// EXPECT_EQ(last_handled, false); +// EXPECT_EQ(results.size(), 1); +// event = &results[0]; +// EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); +// EXPECT_EQ(event->physical, kPhysicalKeyA); +// EXPECT_EQ(event->logical, kLogicalKeyA); +// EXPECT_STREQ(event->character, ""); +// EXPECT_EQ(event->synthesized, false); + +// event->callback(true, event->user_data); +// EXPECT_EQ(last_handled, true); +// results.clear(); + +// // A duplicate A release +// last_handled = false; +// handler->KeyboardHook( +// kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 'a', false, true, +// [&last_handled](bool handled) { last_handled = handled; }); +// EXPECT_EQ(last_handled, true); +// EXPECT_EQ(results.size(), 0); +// } TEST(KeyboardKeyEmbedderHandlerTest, AbruptRepeatIsConvertedtoDown) { + TestKeystate key_state; std::vector results; TestFlutterKeyEvent* event; bool last_handled = false; @@ -559,9 +594,11 @@ TEST(KeyboardKeyEmbedderHandlerTest, AbruptRepeatIsConvertedtoDown) { [&results](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); - }); + }, key_state.Getter()); last_handled = false; + key_state.Set(kVirtualKeyA, true); + // Press A (with was_down true) handler->KeyboardHook( kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, true, @@ -581,6 +618,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, AbruptRepeatIsConvertedtoDown) { // Release A last_handled = false; + key_state.Set(kVirtualKeyA, false); handler->KeyboardHook( kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 'a', false, true, [&last_handled](bool handled) { last_handled = handled; }); From c239f83d8ded11359e36301ca6a8287714e4ab3f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 17 Feb 2021 05:37:50 -0800 Subject: [PATCH 73/82] More tests --- .../windows/keyboard_key_embedder_handler.cc | 6 +- ...keyboard_key_embedder_handler_unittests.cc | 369 ++++++++++++------ 2 files changed, 256 insertions(+), 119 deletions(-) diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index ca8de61d824a8..42ba15209c854 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -205,6 +205,9 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( pressingRecords_.erase(last_logical_record_iter); } + UpdateLastSeenCritialKey(key, physical_key, result_logical_key); + SynchroizeCritialKeys(type == kFlutterKeyEventTypeDown ? key : 0); + if (result_logical_key == VK_PROCESSKEY) { // VK_PROCESSKEY means that the key press is used by an IME. These key // presses are considered handled and not sent to Flutter. These events must @@ -214,9 +217,6 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( return; } - UpdateLastSeenCritialKey(key, physical_key, result_logical_key); - SynchroizeCritialKeys(type == kFlutterKeyEventTypeDown ? key : 0); - FlutterKeyEvent key_data { .struct_size = sizeof(FlutterKeyEvent), .timestamp = diff --git a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index f5137d1b88cde..f5619d847c36f 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -77,12 +77,14 @@ constexpr uint64_t kPhysicalControlLeft = 0x000700e0; constexpr uint64_t kPhysicalControlRight = 0x000700e4; constexpr uint64_t kPhysicalShiftLeft = 0x000700e1; constexpr uint64_t kPhysicalShiftRight = 0x000700e5; +constexpr uint64_t kPhysicalKeyNumLock = 0x00070053; constexpr uint64_t kLogicalKeyA = 0x00000061; constexpr uint64_t kLogicalControlLeft = 0x00300000105; constexpr uint64_t kLogicalControlRight = 0x00400000105; constexpr uint64_t kLogicalShiftLeft = 0x0030000010d; constexpr uint64_t kLogicalShiftRight = 0x0040000010d; +constexpr uint64_t kLogicalKeyNumLock = 0x0000000010a; } // namespace // Test the most basic key events. @@ -467,122 +469,6 @@ TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByVirtualKey) { results.clear(); } -// TEST(KeyboardKeyEmbedderHandlerTest, DuplicateDownIsIgnored) { -// TestKeystate key_state; -// std::vector results; -// TestFlutterKeyEvent* event; -// bool last_handled = false; - -// std::unique_ptr handler = -// std::make_unique( -// [&results](const FlutterKeyEvent& event, -// FlutterKeyEventCallback callback, void* user_data) { -// results.emplace_back(event, callback, user_data); -// }, key_state.Getter()); - -// // Press A -// last_handled = false; -// handler->KeyboardHook( -// kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, -// [&last_handled](bool handled) { last_handled = handled; }); -// EXPECT_EQ(last_handled, false); -// EXPECT_EQ(results.size(), 1); -// event = &results[0]; -// EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); -// EXPECT_EQ(event->physical, kPhysicalKeyA); -// EXPECT_EQ(event->logical, kLogicalKeyA); -// EXPECT_STREQ(event->character, "a"); -// EXPECT_EQ(event->synthesized, false); - -// event->callback(true, event->user_data); -// EXPECT_EQ(last_handled, true); -// results.clear(); -// key_state.Set(kVirtualKeyA, true); - -// // A duplicate A press -// last_handled = false; -// handler->KeyboardHook( -// kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, -// [&last_handled](bool handled) { last_handled = handled; }); -// EXPECT_EQ(last_handled, true); -// EXPECT_EQ(results.size(), 0); - -// // Release A -// last_handled = false; -// handler->KeyboardHook( -// kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 'a', false, true, -// [&last_handled](bool handled) { last_handled = handled; }); -// EXPECT_EQ(last_handled, false); -// EXPECT_EQ(results.size(), 1); -// event = &results[0]; -// EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); -// EXPECT_EQ(event->physical, kPhysicalKeyA); -// EXPECT_EQ(event->logical, kLogicalKeyA); -// EXPECT_STREQ(event->character, ""); -// EXPECT_EQ(event->synthesized, false); - -// event->callback(true, event->user_data); -// EXPECT_EQ(last_handled, true); -// results.clear(); -// } - -// TEST(KeyboardKeyEmbedderHandlerTest, DuplicateUpIsIgnored) { -// std::vector results; -// TestFlutterKeyEvent* event; -// bool last_handled = false; - -// std::unique_ptr handler = -// std::make_unique( -// [&results](const FlutterKeyEvent& event, -// FlutterKeyEventCallback callback, void* user_data) { -// results.emplace_back(event, callback, user_data); -// }); -// last_handled = false; - -// // Press A -// handler->KeyboardHook( -// kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, -// [&last_handled](bool handled) { last_handled = handled; }); -// EXPECT_EQ(last_handled, false); -// EXPECT_EQ(results.size(), 1); -// event = &results[0]; -// EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); -// EXPECT_EQ(event->physical, kPhysicalKeyA); -// EXPECT_EQ(event->logical, kLogicalKeyA); -// EXPECT_STREQ(event->character, "a"); -// EXPECT_EQ(event->synthesized, false); - -// event->callback(true, event->user_data); -// EXPECT_EQ(last_handled, true); -// results.clear(); - -// // Release A -// last_handled = false; -// handler->KeyboardHook( -// kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 'a', false, true, -// [&last_handled](bool handled) { last_handled = handled; }); -// EXPECT_EQ(last_handled, false); -// EXPECT_EQ(results.size(), 1); -// event = &results[0]; -// EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); -// EXPECT_EQ(event->physical, kPhysicalKeyA); -// EXPECT_EQ(event->logical, kLogicalKeyA); -// EXPECT_STREQ(event->character, ""); -// EXPECT_EQ(event->synthesized, false); - -// event->callback(true, event->user_data); -// EXPECT_EQ(last_handled, true); -// results.clear(); - -// // A duplicate A release -// last_handled = false; -// handler->KeyboardHook( -// kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 'a', false, true, -// [&last_handled](bool handled) { last_handled = handled; }); -// EXPECT_EQ(last_handled, true); -// EXPECT_EQ(results.size(), 0); -// } - TEST(KeyboardKeyEmbedderHandlerTest, AbruptRepeatIsConvertedtoDown) { TestKeystate key_state; std::vector results; @@ -636,5 +522,256 @@ TEST(KeyboardKeyEmbedderHandlerTest, AbruptRepeatIsConvertedtoDown) { results.clear(); } +TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncPressingState) { + TestKeystate key_state; + std::vector results; + TestFlutterKeyEvent* event; + bool last_handled = false; + + std::unique_ptr handler = + std::make_unique( + [&results](const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, void* user_data) { + results.emplace_back(event, callback, user_data); + }, + key_state.Getter()); + + // A key down of control left is missed. + key_state.Set(VK_LCONTROL, true); + + // Send a normal event + key_state.Set(kVirtualKeyA, true); + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 2); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalControlLeft); + EXPECT_EQ(event->logical, kLogicalControlLeft); + // EXPECT_STREQ(event->character, ""); // TODO + EXPECT_EQ(event->synthesized, true); + EXPECT_EQ(event->callback, nullptr); + + event = &results[1]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); + EXPECT_STREQ(event->character, "a"); + EXPECT_EQ(event->synthesized, false); + + last_handled = true; + event->callback(false, event->user_data); + EXPECT_EQ(last_handled, false); + results.clear(); + + // A key down of control right is missed. + key_state.Set(VK_LCONTROL, false); + + // Hold KeyA. + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 2); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalControlLeft); + EXPECT_EQ(event->logical, kLogicalControlLeft); + // EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, true); + EXPECT_EQ(event->callback, nullptr); + + event = &results[1]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeRepeat); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); + EXPECT_STREQ(event->character, "a"); + EXPECT_EQ(event->synthesized, false); + + last_handled = true; + event->callback(false, event->user_data); + EXPECT_EQ(last_handled, false); + results.clear(); + + // Release KeyA. + key_state.Set(kVirtualKeyA, false); + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 0, false, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + event->callback(false, event->user_data); +} + +TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledState) { + TestKeystate key_state; + std::vector results; + TestFlutterKeyEvent* event; + bool last_handled = false; + + std::unique_ptr handler = + std::make_unique( + [&results](const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, void* user_data) { + results.emplace_back(event, callback, user_data); + }, + key_state.Getter()); + + // Test if the app starts with NumLock on + key_state.Set(VK_NUMLOCK, false, true); + + // Send a normal event + key_state.Set(kVirtualKeyA, true); + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 3); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalKeyNumLock); + EXPECT_EQ(event->logical, kLogicalKeyNumLock); + // EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, true); + + event = &results[1]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalKeyNumLock); + EXPECT_EQ(event->logical, kLogicalKeyNumLock); + // EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, true); + + event = &results[2]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); + EXPECT_STREQ(event->character, "a"); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); + + // Test if the NumLock is mis-toggled while it should also be pressed + key_state.Set(VK_NUMLOCK, true, true); + + // Send a normal event + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, true); + EXPECT_EQ(results.size(), 2); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalKeyNumLock); + EXPECT_EQ(event->logical, kLogicalKeyNumLock); + // EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, true); + + event = &results[1]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeRepeat); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); + EXPECT_STREQ(event->character, "a"); + EXPECT_EQ(event->synthesized, false); + + event->callback(false, event->user_data); + EXPECT_EQ(last_handled, false); + results.clear(); + + // Numlock is pressed at this moment. + + // Test if the NumLock is mis-toggled while it should also be released + key_state.Set(VK_NUMLOCK, false, false); + + // Send a normal event + key_state.Set(kVirtualKeyA, false); + handler->KeyboardHook( + kVirtualKeyA, kScanCodeKeyA, WM_KEYUP, 0, false, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(results.size(), 4); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalKeyNumLock); + EXPECT_EQ(event->logical, kLogicalKeyNumLock); + // EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, true); + EXPECT_EQ(event->callback, nullptr); + + event = &results[1]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalKeyNumLock); + EXPECT_EQ(event->logical, kLogicalKeyNumLock); + // EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, true); + EXPECT_EQ(event->callback, nullptr); + + event = &results[2]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalKeyNumLock); + EXPECT_EQ(event->logical, kLogicalKeyNumLock); + // EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, true); + EXPECT_EQ(event->callback, nullptr); + + event = &results[3]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + event->callback(false, event->user_data); +} + +TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledStateByItself) { + TestKeystate key_state; + std::vector results; + TestFlutterKeyEvent* event; + bool last_handled = false; + + std::unique_ptr handler = + std::make_unique( + [&results](const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, void* user_data) { + results.emplace_back(event, callback, user_data); + }, + key_state.Getter()); + + // NumLock is on and released + key_state.Set(VK_NUMLOCK, false, true); + + // Send a NumLock key up + handler->KeyboardHook( + VK_NUMLOCK, kScanCodeNumLock, WM_KEYUP, 0, true, true, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 2); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalKeyNumLock); + EXPECT_EQ(event->logical, kLogicalKeyNumLock); + // EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, true); + EXPECT_EQ(event->callback, nullptr); + + event = &results[1]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalKeyNumLock); + EXPECT_EQ(event->logical, kLogicalKeyNumLock); + // EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, true); + + last_handled = false; + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); +} + } // namespace testing } // namespace flutter From cc21634bbb387147470159289b3577b9d5eec83a Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 17 Feb 2021 06:09:57 -0800 Subject: [PATCH 74/82] Fix tests --- .../windows/keyboard_key_embedder_handler.cc | 23 ++++++++++++---- .../windows/keyboard_key_embedder_handler.h | 3 ++- ...keyboard_key_embedder_handler_unittests.cc | 26 +++++++++++++++---- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index 42ba15209c854..5762f10c667b2 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -199,14 +199,16 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( } } + UpdateLastSeenCritialKey(key, physical_key, result_logical_key); + SynchroizeCritialToggledStates(type == kFlutterKeyEventTypeDown ? key : 0); + if (next_has_record) { pressingRecords_[physical_key] = next_logical_record; } else { pressingRecords_.erase(last_logical_record_iter); } - UpdateLastSeenCritialKey(key, physical_key, result_logical_key); - SynchroizeCritialKeys(type == kFlutterKeyEventTypeDown ? key : 0); + SynchroizeCritialPressedStates(); if (result_logical_key == VK_PROCESSKEY) { // VK_PROCESSKEY means that the key press is used by an IME. These key @@ -259,7 +261,7 @@ void KeyboardKeyEmbedderHandler::UpdateLastSeenCritialKey(int virtual_key, uint6 } } -void KeyboardKeyEmbedderHandler::SynchroizeCritialKeys(int toggle_virtual_key) { +void KeyboardKeyEmbedderHandler::SynchroizeCritialToggledStates(int toggle_virtual_key) { for (auto& kv : critical_keys_) { UINT virtual_key = kv.first; CheckedKey& key_info = kv.second; @@ -274,7 +276,6 @@ void KeyboardKeyEmbedderHandler::SynchroizeCritialKeys(int toggle_virtual_key) { if (key_info.check_toggled) { bool should_toggled = state & kStateMaskToggled; if (virtual_key == toggle_virtual_key) { - printf("State %04x\n", state); key_info.toggled_on = !key_info.toggled_on; } if (key_info.toggled_on != should_toggled) { @@ -288,7 +289,7 @@ void KeyboardKeyEmbedderHandler::SynchroizeCritialKeys(int toggle_virtual_key) { empty_character), nullptr, nullptr); } else { - // This key will be pressed in the following synthesized event. + // This key will always be pressed in the following synthesized event. pressingRecords_[key_info.physical_key] = key_info.logical_key; } sendEvent_(SynthesizeSimpleEvent( @@ -300,7 +301,19 @@ void KeyboardKeyEmbedderHandler::SynchroizeCritialKeys(int toggle_virtual_key) { } key_info.toggled_on = should_toggled; } + } +} +void KeyboardKeyEmbedderHandler::SynchroizeCritialPressedStates() { + for (auto& kv : critical_keys_) { + UINT virtual_key = kv.first; + CheckedKey& key_info = kv.second; + if (key_info.physical_key == 0) { + // Never seen this key. + continue; + } + assert(key_info.logical_key != 0); + SHORT state = get_key_state_(virtual_key); if (key_info.check_pressed) { auto recorded_pressed_iter = pressingRecords_.find(key_info.physical_key); bool recorded_pressed = recorded_pressed_iter != pressingRecords_.end(); diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index 1d7806810589f..c729b9d24d413 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -70,7 +70,8 @@ class KeyboardKeyEmbedderHandler void InitCheckedKeys(); void UpdateLastSeenCritialKey(int virtual_key, uint64_t physical_key, uint64_t logical_key); - void SynchroizeCritialKeys(int this_virtual_key); + void SynchroizeCritialToggledStates(int this_virtual_key); + void SynchroizeCritialPressedStates(); std::function sendEvent_; diff --git a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index f5619d847c36f..6edcf5847bf40 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -744,17 +744,25 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledStateByItself) { }, key_state.Getter()); - // NumLock is on and released - key_state.Set(VK_NUMLOCK, false, true); + // When NumLock is down + key_state.Set(VK_NUMLOCK, true, true); + handler->KeyboardHook( + VK_NUMLOCK, kScanCodeNumLock, WM_KEYDOWN, 0, true, false, + [&last_handled](bool handled) { last_handled = handled; }); + event = &results.back(); + event->callback(false, event->user_data); + results.clear(); + // Numlock is desynchronized by being off and released + key_state.Set(VK_NUMLOCK, false, false); // Send a NumLock key up handler->KeyboardHook( VK_NUMLOCK, kScanCodeNumLock, WM_KEYUP, 0, true, true, [&last_handled](bool handled) { last_handled = handled; }); EXPECT_EQ(last_handled, false); - EXPECT_EQ(results.size(), 2); + EXPECT_EQ(results.size(), 3); event = &results[0]; - EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); EXPECT_EQ(event->physical, kPhysicalKeyNumLock); EXPECT_EQ(event->logical, kLogicalKeyNumLock); // EXPECT_STREQ(event->character, ""); @@ -762,11 +770,19 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledStateByItself) { EXPECT_EQ(event->callback, nullptr); event = &results[1]; - EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); EXPECT_EQ(event->physical, kPhysicalKeyNumLock); EXPECT_EQ(event->logical, kLogicalKeyNumLock); // EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, true); + EXPECT_EQ(event->callback, nullptr); + + event = &results[2]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalKeyNumLock); + EXPECT_EQ(event->logical, kLogicalKeyNumLock); + // EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); last_handled = false; event->callback(true, event->user_data); From 4b0d8412f8517b9985014695293d44d9e719ada6 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 17 Feb 2021 06:13:03 -0800 Subject: [PATCH 75/82] format --- .../platform/windows/flutter_window_winuwp.cc | 8 +- .../windows/keyboard_key_embedder_handler.cc | 136 ++++++++++-------- .../windows/keyboard_key_embedder_handler.h | 21 ++- ...keyboard_key_embedder_handler_unittests.cc | 32 +++-- .../platform/windows/keyboard_key_handler.cc | 60 ++++---- shell/platform/windows/keyboard_key_handler.h | 4 +- .../windows/keyboard_key_handler_unittests.cc | 38 ++--- .../windows/win32_flutter_window_unittests.cc | 20 +-- shell/platform/windows/win32_window.cc | 5 +- 9 files changed, 181 insertions(+), 143 deletions(-) diff --git a/shell/platform/windows/flutter_window_winuwp.cc b/shell/platform/windows/flutter_window_winuwp.cc index a680b369467a3..8c914329431df 100644 --- a/shell/platform/windows/flutter_window_winuwp.cc +++ b/shell/platform/windows/flutter_window_winuwp.cc @@ -268,8 +268,8 @@ void FlutterWindowWinUWP::OnKeyUp( int key = static_cast(args.VirtualKey()); char32_t chararacter = static_cast(key | 32); int action = 0x0101; - binding_handler_delegate_->OnKey(key, scancode, action, chararacter, false /* extended */, - true /* was_down */); + binding_handler_delegate_->OnKey(key, scancode, action, chararacter, + false /* extended */, true /* was_down */); } void FlutterWindowWinUWP::OnKeyDown( @@ -284,8 +284,8 @@ void FlutterWindowWinUWP::OnKeyDown( int key = static_cast(args.VirtualKey()); char32_t chararacter = static_cast(key | 32); int action = 0x0100; - binding_handler_delegate_->OnKey(key, scancode, action, chararacter, false /* extended */, - false /* was_down */); + binding_handler_delegate_->OnKey(key, scancode, action, chararacter, + false /* extended */, false /* was_down */); } void FlutterWindowWinUWP::OnCharacterReceived( diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index 5762f10c667b2..d60af3cf3fabd 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -59,8 +59,7 @@ KeyboardKeyEmbedderHandler::KeyboardKeyEmbedderHandler( std::function send_event, - GetKeyStateHandler get_key_state - ) + GetKeyStateHandler get_key_state) : sendEvent_(send_event), get_key_state_(get_key_state), response_id_(1) { InitCheckedKeys(); } @@ -219,18 +218,17 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( return; } - FlutterKeyEvent key_data { - .struct_size = sizeof(FlutterKeyEvent), - .timestamp = - static_cast( - std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()) - .count()), - .type = type, - .physical = physical_key, - .logical = result_logical_key, - .character = character_bytes, - .synthesized = false, + FlutterKeyEvent key_data{ + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = static_cast( + std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count()), + .type = type, + .physical = physical_key, + .logical = result_logical_key, + .character = character_bytes, + .synthesized = false, }; response_id_ += 1; @@ -253,7 +251,10 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( reinterpret_cast(pending_responses_[response_id].get())); } -void KeyboardKeyEmbedderHandler::UpdateLastSeenCritialKey(int virtual_key, uint64_t physical_key, uint64_t logical_key) { +void KeyboardKeyEmbedderHandler::UpdateLastSeenCritialKey( + int virtual_key, + uint64_t physical_key, + uint64_t logical_key) { auto found = critical_keys_.find(virtual_key); if (found != critical_keys_.end()) { found->second.physical_key = physical_key; @@ -261,7 +262,8 @@ void KeyboardKeyEmbedderHandler::UpdateLastSeenCritialKey(int virtual_key, uint6 } } -void KeyboardKeyEmbedderHandler::SynchroizeCritialToggledStates(int toggle_virtual_key) { +void KeyboardKeyEmbedderHandler::SynchroizeCritialToggledStates( + int toggle_virtual_key) { for (auto& kv : critical_keys_) { UINT virtual_key = kv.first; CheckedKey& key_info = kv.second; @@ -281,23 +283,20 @@ void KeyboardKeyEmbedderHandler::SynchroizeCritialToggledStates(int toggle_virtu if (key_info.toggled_on != should_toggled) { const char* empty_character = ""; // If the key is pressed, release it first. - if (pressingRecords_.find(key_info.physical_key) != pressingRecords_.end()) { + if (pressingRecords_.find(key_info.physical_key) != + pressingRecords_.end()) { sendEvent_(SynthesizeSimpleEvent( - kFlutterKeyEventTypeUp, - key_info.physical_key, - key_info.logical_key, - empty_character), - nullptr, nullptr); + kFlutterKeyEventTypeUp, key_info.physical_key, + key_info.logical_key, empty_character), + nullptr, nullptr); } else { // This key will always be pressed in the following synthesized event. pressingRecords_[key_info.physical_key] = key_info.logical_key; } - sendEvent_(SynthesizeSimpleEvent( - kFlutterKeyEventTypeDown, - key_info.physical_key, - key_info.logical_key, - empty_character), - nullptr, nullptr); + sendEvent_(SynthesizeSimpleEvent(kFlutterKeyEventTypeDown, + key_info.physical_key, + key_info.logical_key, empty_character), + nullptr, nullptr); } key_info.toggled_on = should_toggled; } @@ -325,12 +324,12 @@ void KeyboardKeyEmbedderHandler::SynchroizeCritialPressedStates() { pressingRecords_.erase(recorded_pressed_iter); } const char* empty_character = ""; - sendEvent_(SynthesizeSimpleEvent( - should_pressed ? kFlutterKeyEventTypeDown : kFlutterKeyEventTypeUp, - key_info.physical_key, - key_info.logical_key, - empty_character), - nullptr, nullptr); + sendEvent_( + SynthesizeSimpleEvent(should_pressed ? kFlutterKeyEventTypeDown + : kFlutterKeyEventTypeUp, + key_info.physical_key, key_info.logical_key, + empty_character), + nullptr, nullptr); } } } @@ -343,25 +342,36 @@ void KeyboardKeyEmbedderHandler::HandleResponse(bool handled, void* user_data) { } void KeyboardKeyEmbedderHandler::InitCheckedKeys() { - auto createCheckedKey = [this](UINT virtual_key, bool extended, bool check_pressed, bool check_toggled) -> CheckedKey { + auto createCheckedKey = [this](UINT virtual_key, bool extended, + bool check_pressed, + bool check_toggled) -> CheckedKey { UINT scan_code = MapVirtualKey(virtual_key, MAPVK_VK_TO_VSC); return CheckedKey{ - .physical_key = getPhysicalKey(scan_code, extended), - .logical_key = getLogicalKey(virtual_key, extended, scan_code), - .check_pressed = check_pressed, - .check_toggled = check_toggled, - .toggled_on = check_toggled ? !!(get_key_state_(virtual_key) & kStateMaskToggled) : false, + .physical_key = getPhysicalKey(scan_code, extended), + .logical_key = getLogicalKey(virtual_key, extended, scan_code), + .check_pressed = check_pressed, + .check_toggled = check_toggled, + .toggled_on = check_toggled + ? !!(get_key_state_(virtual_key) & kStateMaskToggled) + : false, }; }; - critical_keys_.emplace(VK_LSHIFT, createCheckedKey(VK_LSHIFT, false, true, false)); - critical_keys_.emplace(VK_RSHIFT, createCheckedKey(VK_RSHIFT, false, true, false)); - critical_keys_.emplace(VK_LCONTROL, createCheckedKey(VK_LCONTROL, false, true, false)); - critical_keys_.emplace(VK_RCONTROL, createCheckedKey(VK_RCONTROL, true, true, false)); - - critical_keys_.emplace(VK_CAPITAL, createCheckedKey(VK_CAPITAL, false, true, true)); - critical_keys_.emplace(VK_SCROLL, createCheckedKey(VK_SCROLL, false, true, true)); - critical_keys_.emplace(VK_NUMLOCK, createCheckedKey(VK_NUMLOCK, true, true, true)); + critical_keys_.emplace(VK_LSHIFT, + createCheckedKey(VK_LSHIFT, false, true, false)); + critical_keys_.emplace(VK_RSHIFT, + createCheckedKey(VK_RSHIFT, false, true, false)); + critical_keys_.emplace(VK_LCONTROL, + createCheckedKey(VK_LCONTROL, false, true, false)); + critical_keys_.emplace(VK_RCONTROL, + createCheckedKey(VK_RCONTROL, true, true, false)); + + critical_keys_.emplace(VK_CAPITAL, + createCheckedKey(VK_CAPITAL, false, true, true)); + critical_keys_.emplace(VK_SCROLL, + createCheckedKey(VK_SCROLL, false, true, true)); + critical_keys_.emplace(VK_NUMLOCK, + createCheckedKey(VK_NUMLOCK, true, true, true)); } void KeyboardKeyEmbedderHandler::ConvertUtf32ToUtf8_(char* out, char32_t ch) { @@ -374,21 +384,23 @@ void KeyboardKeyEmbedderHandler::ConvertUtf32ToUtf8_(char* out, char32_t ch) { strcpy_s(out, kCharacterCacheSize, Utf8FromUtf16(text).c_str()); } -FlutterKeyEvent KeyboardKeyEmbedderHandler::SynthesizeSimpleEvent(FlutterKeyEventType type, uint64_t physical, uint64_t logical, const char* character) { - return FlutterKeyEvent { - .struct_size = sizeof(FlutterKeyEvent), - .timestamp = - static_cast( - std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()) - .count()), - .type = type, - .physical = physical, - .logical = logical, - .character = character, - .synthesized = true, +FlutterKeyEvent KeyboardKeyEmbedderHandler::SynthesizeSimpleEvent( + FlutterKeyEventType type, + uint64_t physical, + uint64_t logical, + const char* character) { + return FlutterKeyEvent{ + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = static_cast( + std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count()), + .type = type, + .physical = physical, + .logical = logical, + .character = character, + .synthesized = true, }; } - } // namespace flutter diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index c729b9d24d413..20efba5918b14 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -15,8 +15,7 @@ namespace flutter { -namespace { -} // namespace +namespace {} // namespace // A delegate of |KeyboardKeyHandler| that handles events by sending // converted |FlutterKeyEvent|s through the embedder API. @@ -35,7 +34,8 @@ class KeyboardKeyEmbedderHandler // Use `send_event` to define how the manager should dispatch converted // flutter events, as well as how to receive the resopnse, to the engine. It's // typically FlutterWindowsEngine::SendKeyEvent. - explicit KeyboardKeyEmbedderHandler(SendEvent send_event, GetKeyStateHandler get_key_state); + explicit KeyboardKeyEmbedderHandler(SendEvent send_event, + GetKeyStateHandler get_key_state); virtual ~KeyboardKeyEmbedderHandler(); @@ -60,7 +60,8 @@ class KeyboardKeyEmbedderHandler uint64_t physical_key; uint64_t logical_key; - // Whether to ensure the pressing state of the key (usually for modifier keys). + // Whether to ensure the pressing state of the key (usually for modifier + // keys). bool check_pressed; // Whether to ensure the toggled state of the key (usually for lock keys). bool check_toggled; @@ -69,7 +70,9 @@ class KeyboardKeyEmbedderHandler }; void InitCheckedKeys(); - void UpdateLastSeenCritialKey(int virtual_key, uint64_t physical_key, uint64_t logical_key); + void UpdateLastSeenCritialKey(int virtual_key, + uint64_t physical_key, + uint64_t logical_key); void SynchroizeCritialToggledStates(int this_virtual_key); void SynchroizeCritialPressedStates(); @@ -77,7 +80,8 @@ class KeyboardKeyEmbedderHandler sendEvent_; GetKeyStateHandler get_key_state_; - // A map from physical keys to logical keys, each entry indicating a pressed key. + // A map from physical keys to logical keys, each entry indicating a pressed + // key. std::map pressingRecords_; std::map> pending_responses_; uint64_t response_id_; @@ -93,7 +97,10 @@ class KeyboardKeyEmbedderHandler static uint64_t getLogicalKey(int key, bool extended, int scancode); static void HandleResponse(bool handled, void* user_data); static void ConvertUtf32ToUtf8_(char* out, char32_t ch); - static FlutterKeyEvent SynthesizeSimpleEvent(FlutterKeyEventType type, uint64_t physical, uint64_t logical, const char* character); + static FlutterKeyEvent SynthesizeSimpleEvent(FlutterKeyEventType type, + uint64_t physical, + uint64_t logical, + const char* character); static std::map windowsToPhysicalMap_; static std::map windowsToLogicalMap_; diff --git a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index 6edcf5847bf40..e1ffecf72ed43 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -43,15 +43,14 @@ class TestFlutterKeyEvent : public FlutterKeyEvent { class TestKeystate { public: void Set(int virtual_key, bool pressed, bool toggled_on = false) { - state_[virtual_key] = (pressed ? kStateMaskPressed : 0) | (toggled_on ? kStateMaskToggled : 0); + state_[virtual_key] = (pressed ? kStateMaskPressed : 0) | + (toggled_on ? kStateMaskToggled : 0); } - SHORT Get(int virtual_key) { - return state_[virtual_key]; - } + SHORT Get(int virtual_key) { return state_[virtual_key]; } KeyboardKeyEmbedderHandler::GetKeyStateHandler Getter() { - return [this](int virtual_key) {return Get(virtual_key);}; + return [this](int virtual_key) { return Get(virtual_key); }; } private: @@ -102,7 +101,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, BasicKeyPressingAndHolding) { FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); }, - key_state.Getter()); + key_state.Getter()); // Press KeyA. handler->KeyboardHook( @@ -169,7 +168,8 @@ TEST(KeyboardKeyEmbedderHandlerTest, ToggleNumLockDuringNumpadPress) { [&results](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); - }, key_state.Getter()); + }, + key_state.Getter()); // Press NumPad1. key_state.Set(VK_NUMPAD1, true); @@ -240,7 +240,8 @@ TEST(KeyboardKeyEmbedderHandlerTest, ImeEventsAreIgnored) { [&results](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); - }, key_state.Getter()); + }, + key_state.Getter()); // Press A in an IME last_handled = false; @@ -298,7 +299,8 @@ TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByExtendedBit) { [&results](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); - }, key_state.Getter()); + }, + key_state.Getter()); // Press Ctrl left. last_handled = false; @@ -390,7 +392,8 @@ TEST(KeyboardKeyEmbedderHandlerTest, ModifierKeysByVirtualKey) { [&results](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); - }, key_state.Getter()); + }, + key_state.Getter()); // Press Shift left. last_handled = false; @@ -480,7 +483,8 @@ TEST(KeyboardKeyEmbedderHandlerTest, AbruptRepeatIsConvertedtoDown) { [&results](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); - }, key_state.Getter()); + }, + key_state.Getter()); last_handled = false; key_state.Set(kVirtualKeyA, true); @@ -534,7 +538,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncPressingState) { FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); }, - key_state.Getter()); + key_state.Getter()); // A key down of control left is missed. key_state.Set(VK_LCONTROL, true); @@ -622,7 +626,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledState) { FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); }, - key_state.Getter()); + key_state.Getter()); // Test if the app starts with NumLock on key_state.Set(VK_NUMLOCK, false, true); @@ -742,7 +746,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledStateByItself) { FlutterKeyEventCallback callback, void* user_data) { results.emplace_back(event, callback, user_data); }, - key_state.Getter()); + key_state.Getter()); // When NumLock is down key_state.Set(VK_NUMLOCK, true, true); diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index e2c5dbc25f70d..d21a9f9577324 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -61,13 +61,16 @@ void KeyboardKeyHandler::RedispatchEvent(std::unique_ptr event) { char32_t character = event->character; INPUT input_event{ - .type = INPUT_KEYBOARD, - .ki = KEYBDINPUT{ - .wVk = 0, - .wScan = static_cast(event->scancode), - .dwFlags = static_cast(KEYEVENTF_SCANCODE | (event->extended ? KEYEVENTF_EXTENDEDKEY : 0x0) | - (event->action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0)), - }, + .type = INPUT_KEYBOARD, + .ki = + KEYBDINPUT{ + .wVk = 0, + .wScan = static_cast(event->scancode), + .dwFlags = static_cast( + KEYEVENTF_SCANCODE | + (event->extended ? KEYEVENTF_EXTENDEDKEY : 0x0) | + (event->action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0)), + }, }; pending_redispatches_.push_back(std::move(event)); @@ -76,8 +79,7 @@ void KeyboardKeyHandler::RedispatchEvent(std::unique_ptr event) { if (accepted != 1) { std::cerr << "Unable to synthesize event for unhandled keyboard event " "with scancode " - << scancode << " (character " << character - << ")" << std::endl; + << scancode << " (character " << character << ")" << std::endl; } } @@ -88,14 +90,15 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, char32_t character, bool extended, bool was_down) { - std::unique_ptr incoming = std::make_unique(PendingEvent{ - .key = static_cast(key), - .scancode = static_cast(scancode), - .action = static_cast(action), - .character = character, - .extended = extended, - .was_down = was_down, - }); + std::unique_ptr incoming = + std::make_unique(PendingEvent{ + .key = static_cast(key), + .scancode = static_cast(scancode), + .action = static_cast(action), + .character = character, + .extended = extended, + .was_down = was_down, + }); incoming->hash = ComputeEventHash(*incoming); if (RemoveRedispatchedEvent(*incoming)) { @@ -116,11 +119,10 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, pending_responds_.push_back(std::move(incoming)); for (const auto& delegate : delegates_) { - delegate->KeyboardHook( - key, scancode, action, character, extended, was_down, - [sequence_id, this](bool handled) { - ResolvePendingEvent(sequence_id, handled); - }); + delegate->KeyboardHook(key, scancode, action, character, extended, was_down, + [sequence_id, this](bool handled) { + ResolvePendingEvent(sequence_id, handled); + }); } // |ResolvePendingEvent| might trigger redispatching synchronously, @@ -133,17 +135,19 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, } bool KeyboardKeyHandler::RemoveRedispatchedEvent(const PendingEvent& incoming) { - for (auto iter = pending_redispatches_.begin(); iter != pending_redispatches_.end(); - ++iter) { + for (auto iter = pending_redispatches_.begin(); + iter != pending_redispatches_.end(); ++iter) { if ((*iter)->hash == incoming.hash) { pending_redispatches_.erase(iter); return true; } } - return false;; + return false; + ; } -void KeyboardKeyHandler::ResolvePendingEvent(uint64_t sequence_id, bool handled) { +void KeyboardKeyHandler::ResolvePendingEvent(uint64_t sequence_id, + bool handled) { // Find the pending event for (auto iter = pending_responds_.begin(); iter != pending_responds_.end(); ++iter) { @@ -185,8 +189,8 @@ uint64_t KeyboardKeyHandler::ComputeEventHash(const PendingEvent& event) { // Calculate a key event ID based on the scan code of the key pressed, // and the flags we care about. return event.scancode | (((event.action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0) | - (event.extended ? KEYEVENTF_EXTENDEDKEY : 0x0)) - << 16); + (event.extended ? KEYEVENTF_EXTENDEDKEY : 0x0)) + << 16); } } // namespace flutter diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index 9ca319659041b..f7f41e892a9b6 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -162,8 +162,8 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { // Another alternative would be to compute a checksum from all the data in the // event (just compute it over the bytes in the struct, probably skipping // timestamps), but the fields used are enough to differentiate them, and - // since Windows does some processing on the events (coming up with virtual key - // codes, setting timestamps, etc.), it's not clear that the redispatched + // since Windows does some processing on the events (coming up with virtual + // key codes, setting timestamps, etc.), it's not clear that the redispatched // events would have the same checksums. static uint64_t ComputeEventHash(const PendingEvent& event); }; diff --git a/shell/platform/windows/keyboard_key_handler_unittests.cc b/shell/platform/windows/keyboard_key_handler_unittests.cc index 13cce1fa2fda8..267c1d0dff440 100644 --- a/shell/platform/windows/keyboard_key_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_handler_unittests.cc @@ -18,11 +18,15 @@ static constexpr int kHandledScanCode = 20; static constexpr int kHandledScanCode2 = 22; static constexpr int kUnhandledScanCode = 21; -typedef std::function Callback; -typedef std::function CallbackHandler; +typedef std::function Callback; +typedef std::function CallbackHandler; void dont_respond(Callback& callback) {} -void respond_true(Callback& callback) { callback(true); } -void respond_false(Callback& callback) { callback(false); } +void respond_true(Callback& callback) { + callback(true); +} +void respond_false(Callback& callback) { + callback(false); +} // A testing |KeyHandlerDelegate| that records all calls // to |KeyboardHook| and can be customized with whether @@ -30,7 +34,6 @@ void respond_false(Callback& callback) { callback(false); } class MockKeyHandlerDelegate : public KeyboardKeyHandler::KeyboardKeyHandlerDelegate { public: - class KeyboardHookCall { public: int delegate_id; @@ -56,7 +59,8 @@ class MockKeyHandlerDelegate // Defaults to always returning true (async). MockKeyHandlerDelegate(int delegate_id, std::list* hook_history) - : delegate_id(delegate_id), hook_history(hook_history), + : delegate_id(delegate_id), + hook_history(hook_history), callback_handler(dont_respond) {} virtual ~MockKeyHandlerDelegate() = default; @@ -89,9 +93,7 @@ class TestKeyboardKeyHandler : public KeyboardKeyHandler { explicit TestKeyboardKeyHandler(EventRedispatcher redispatch_event) : KeyboardKeyHandler(redispatch_event) {} - bool HasRedispatched() { - return RedispatchedCount() > 0; - } + bool HasRedispatched() { return RedispatchedCount() > 0; } }; } // namespace @@ -341,10 +343,10 @@ TEST(KeyboardKeyHandlerTest, WithTwoAsyncDelegates) { // Regression test for a crash in an earlier implementation. // -// In real life, the framework responses slowly. The next real event might arrive -// earlier than the framework response, and if the 2nd event is identical to the -// one waiting for response, an earlier implementation will crash upon the -// response. +// In real life, the framework responses slowly. The next real event might +// arrive earlier than the framework response, and if the 2nd event is identical +// to the one waiting for response, an earlier implementation will crash upon +// the response. TEST(KeyboardKeyHandlerTest, WithSlowFrameworkResponse) { std::list hook_history; @@ -364,13 +366,15 @@ TEST(KeyboardKeyHandlerTest, WithSlowFrameworkResponse) { handler.AddDelegate(std::move(delegate1)); // The first native event. - EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, - WM_KEYDOWN, L'a', false, true), true); + EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, + L'a', false, true), + true); // The second identical native event, received between the first and its // framework response. - EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, - WM_KEYDOWN, L'a', false, true), true); + EXPECT_EQ(handler.KeyboardHook(nullptr, 64, kHandledScanCode, WM_KEYDOWN, + L'a', false, true), + true); EXPECT_EQ(redispatch_scancode, 0); EXPECT_EQ(hook_history.size(), 2); diff --git a/shell/platform/windows/win32_flutter_window_unittests.cc b/shell/platform/windows/win32_flutter_window_unittests.cc index 0a51324904a92..6fb3afcd26f0a 100644 --- a/shell/platform/windows/win32_flutter_window_unittests.cc +++ b/shell/platform/windows/win32_flutter_window_unittests.cc @@ -51,9 +51,11 @@ struct SimulatedEvent { // key event handler. class SpyKeyboardKeyHandler : public KeyboardHandlerBase { public: - SpyKeyboardKeyHandler(flutter::BinaryMessenger* messenger, - KeyboardKeyHandler::EventRedispatcher redispatch_event) { - real_implementation_ = std::make_unique(redispatch_event); + SpyKeyboardKeyHandler( + flutter::BinaryMessenger* messenger, + KeyboardKeyHandler::EventRedispatcher redispatch_event) { + real_implementation_ = + std::make_unique(redispatch_event); real_implementation_->AddDelegate( std::make_unique(messenger)); ON_CALL(*this, KeyboardHook(_, _, _, _, _, _, _)) @@ -201,7 +203,8 @@ class TestFlutterWindowsView : public FlutterWindowsView { // Windows will fill it in). pending_responds_.push_back(SimulatedEvent{message, virtual_key_, lparam}); if (is_printable_ && (kbdinput.dwFlags & KEYEVENTF_KEYUP) == 0) { - pending_responds_.push_back(SimulatedEvent{WM_CHAR, virtual_key_, lparam}); + pending_responds_.push_back( + SimulatedEvent{WM_CHAR, virtual_key_, lparam}); } return 1; } @@ -302,7 +305,8 @@ TEST(Win32FlutterWindowTest, NonPrintableKeyDownPropagation) { TestFlutterWindowsView flutter_windows_view( std::move(window_binding_handler), virtual_key, false /* is_printable */); win32window.SetView(&flutter_windows_view); - LPARAM lparam = CreateKeyEventLparam(scan_code, false /* extended */, false /* PrevState */); + LPARAM lparam = CreateKeyEventLparam(scan_code, false /* extended */, + false /* PrevState */); // Test an event not handled by the framework { @@ -317,8 +321,7 @@ TEST(Win32FlutterWindowTest, NonPrintableKeyDownPropagation) { KeyboardHook(_, _, _, _, _, _, _)) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(win32window, - DefaultWindowProc(_, _, _, _)) + EXPECT_CALL(win32window, DefaultWindowProc(_, _, _, _)) .Times(0) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.key_event_handler, TextHook(_, _)) @@ -363,7 +366,8 @@ TEST(Win32FlutterWindowTest, CharKeyDownPropagation) { TestFlutterWindowsView flutter_windows_view( std::move(window_binding_handler), virtual_key, true /* is_printable */); win32window.SetView(&flutter_windows_view); - LPARAM lparam = CreateKeyEventLparam(scan_code, false /* extended */, true /* PrevState */); + LPARAM lparam = CreateKeyEventLparam(scan_code, false /* extended */, + true /* PrevState */); // Test an event not handled by the framework { diff --git a/shell/platform/windows/win32_window.cc b/shell/platform/windows/win32_window.cc index b5f0c998185bf..a8957b9819776 100644 --- a/shell/platform/windows/win32_window.cc +++ b/shell/platform/windows/win32_window.cc @@ -431,7 +431,10 @@ Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { GetWindowLongPtr(window, GWLP_USERDATA)); } -LRESULT Win32Window::DefaultWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { +LRESULT Win32Window::DefaultWindowProc(HWND hWnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam) { return DefWindowProc(hWnd, Msg, wParam, lParam); } From d6b692043039fa95bae64febc6be3342262e1a21 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 17 Feb 2021 16:56:03 -0800 Subject: [PATCH 76/82] Fix move and test --- ...keyboard_key_embedder_handler_unittests.cc | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index e1ffecf72ed43..5337033153e92 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -5,6 +5,7 @@ #include "flutter/shell/platform/windows/keyboard_key_embedder_handler.h" #include +#include #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" @@ -33,6 +34,14 @@ class TestFlutterKeyEvent : public FlutterKeyEvent { synthesized = src.synthesized; } + TestFlutterKeyEvent(TestFlutterKeyEvent&& source) : + FlutterKeyEvent(source), + callback(std::move(source.callback)), + user_data(source.user_data) + { + character = character_str.c_str(); + } + FlutterKeyEventCallback callback; void* user_data; @@ -554,7 +563,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncPressingState) { EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); EXPECT_EQ(event->physical, kPhysicalControlLeft); EXPECT_EQ(event->logical, kLogicalControlLeft); - // EXPECT_STREQ(event->character, ""); // TODO + EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, true); EXPECT_EQ(event->callback, nullptr); @@ -583,7 +592,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncPressingState) { EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); EXPECT_EQ(event->physical, kPhysicalControlLeft); EXPECT_EQ(event->logical, kLogicalControlLeft); - // EXPECT_STREQ(event->character, ""); + EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, true); EXPECT_EQ(event->callback, nullptr); @@ -642,14 +651,14 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledState) { EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); EXPECT_EQ(event->physical, kPhysicalKeyNumLock); EXPECT_EQ(event->logical, kLogicalKeyNumLock); - // EXPECT_STREQ(event->character, ""); + EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, true); event = &results[1]; EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); EXPECT_EQ(event->physical, kPhysicalKeyNumLock); EXPECT_EQ(event->logical, kLogicalKeyNumLock); - // EXPECT_STREQ(event->character, ""); + EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, true); event = &results[2]; @@ -676,7 +685,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledState) { EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); EXPECT_EQ(event->physical, kPhysicalKeyNumLock); EXPECT_EQ(event->logical, kLogicalKeyNumLock); - // EXPECT_STREQ(event->character, ""); + EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, true); event = &results[1]; @@ -705,7 +714,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledState) { EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); EXPECT_EQ(event->physical, kPhysicalKeyNumLock); EXPECT_EQ(event->logical, kLogicalKeyNumLock); - // EXPECT_STREQ(event->character, ""); + EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, true); EXPECT_EQ(event->callback, nullptr); @@ -713,7 +722,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledState) { EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); EXPECT_EQ(event->physical, kPhysicalKeyNumLock); EXPECT_EQ(event->logical, kLogicalKeyNumLock); - // EXPECT_STREQ(event->character, ""); + EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, true); EXPECT_EQ(event->callback, nullptr); @@ -721,7 +730,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledState) { EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); EXPECT_EQ(event->physical, kPhysicalKeyNumLock); EXPECT_EQ(event->logical, kLogicalKeyNumLock); - // EXPECT_STREQ(event->character, ""); + EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, true); EXPECT_EQ(event->callback, nullptr); @@ -769,7 +778,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledStateByItself) { EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); EXPECT_EQ(event->physical, kPhysicalKeyNumLock); EXPECT_EQ(event->logical, kLogicalKeyNumLock); - // EXPECT_STREQ(event->character, ""); + EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, true); EXPECT_EQ(event->callback, nullptr); @@ -777,7 +786,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledStateByItself) { EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); EXPECT_EQ(event->physical, kPhysicalKeyNumLock); EXPECT_EQ(event->logical, kLogicalKeyNumLock); - // EXPECT_STREQ(event->character, ""); + EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, true); EXPECT_EQ(event->callback, nullptr); @@ -785,7 +794,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledStateByItself) { EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); EXPECT_EQ(event->physical, kPhysicalKeyNumLock); EXPECT_EQ(event->logical, kLogicalKeyNumLock); - // EXPECT_STREQ(event->character, ""); + EXPECT_STREQ(event->character, ""); EXPECT_EQ(event->synthesized, false); last_handled = false; From 0331bf5ef92c502af81e6abbd4059f535b8fb769 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 17 Feb 2021 16:56:57 -0800 Subject: [PATCH 77/82] format --- .../keyboard_key_embedder_handler_unittests.cc | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index 5337033153e92..58362f3fb4065 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -34,13 +34,12 @@ class TestFlutterKeyEvent : public FlutterKeyEvent { synthesized = src.synthesized; } - TestFlutterKeyEvent(TestFlutterKeyEvent&& source) : - FlutterKeyEvent(source), - callback(std::move(source.callback)), - user_data(source.user_data) - { - character = character_str.c_str(); - } + TestFlutterKeyEvent(TestFlutterKeyEvent&& source) + : FlutterKeyEvent(source), + callback(std::move(source.callback)), + user_data(source.user_data) { + character = character_str.c_str(); + } FlutterKeyEventCallback callback; void* user_data; From e5aecc4442318cb0928ddf3a11347778d2625399 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 17 Feb 2021 17:23:21 -0800 Subject: [PATCH 78/82] Renames and docs --- .../windows/keyboard_key_embedder_handler.cc | 14 ++++----- .../windows/keyboard_key_embedder_handler.h | 29 ++++++++++++++----- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index d60af3cf3fabd..39e31267a9686 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -61,7 +61,7 @@ KeyboardKeyEmbedderHandler::KeyboardKeyEmbedderHandler( void* user_data)> send_event, GetKeyStateHandler get_key_state) : sendEvent_(send_event), get_key_state_(get_key_state), response_id_(1) { - InitCheckedKeys(); + InitCriticalKeys(); } KeyboardKeyEmbedderHandler::~KeyboardKeyEmbedderHandler() = default; @@ -266,7 +266,7 @@ void KeyboardKeyEmbedderHandler::SynchroizeCritialToggledStates( int toggle_virtual_key) { for (auto& kv : critical_keys_) { UINT virtual_key = kv.first; - CheckedKey& key_info = kv.second; + CriticalKey& key_info = kv.second; if (key_info.physical_key == 0) { // Never seen this key. continue; @@ -306,7 +306,7 @@ void KeyboardKeyEmbedderHandler::SynchroizeCritialToggledStates( void KeyboardKeyEmbedderHandler::SynchroizeCritialPressedStates() { for (auto& kv : critical_keys_) { UINT virtual_key = kv.first; - CheckedKey& key_info = kv.second; + CriticalKey& key_info = kv.second; if (key_info.physical_key == 0) { // Never seen this key. continue; @@ -341,15 +341,15 @@ void KeyboardKeyEmbedderHandler::HandleResponse(bool handled, void* user_data) { callback(handled, pending->response_id); } -void KeyboardKeyEmbedderHandler::InitCheckedKeys() { +void KeyboardKeyEmbedderHandler::InitCriticalKeys() { auto createCheckedKey = [this](UINT virtual_key, bool extended, bool check_pressed, - bool check_toggled) -> CheckedKey { + bool check_toggled) -> CriticalKey { UINT scan_code = MapVirtualKey(virtual_key, MAPVK_VK_TO_VSC); - return CheckedKey{ + return CriticalKey{ .physical_key = getPhysicalKey(scan_code, extended), .logical_key = getLogicalKey(virtual_key, extended, scan_code), - .check_pressed = check_pressed, + .check_pressed = check_pressed || check_toggled, .check_toggled = check_toggled, .toggled_on = check_toggled ? !!(get_key_state_(virtual_key) & kStateMaskToggled) diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index 20efba5918b14..efcb7cdb4a9e6 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -31,9 +31,13 @@ class KeyboardKeyEmbedderHandler // Build a KeyboardKeyEmbedderHandler. // - // Use `send_event` to define how the manager should dispatch converted - // flutter events, as well as how to receive the resopnse, to the engine. It's - // typically FlutterWindowsEngine::SendKeyEvent. + // Use `send_event` to define how the class should dispatch converted + // flutter events, as well as how to receive the response, to the engine. It's + // typically FlutterWindowsEngine::SendKeyEvent. The 2nd and 3rd parameter + // of the call might be nullptr. + // + // Use `get_key_state` to define how the class should get a reliable result of + // the state for a virtual key. It's typically Win32's GetKeyState. explicit KeyboardKeyEmbedderHandler(SendEvent send_event, GetKeyStateHandler get_key_state); @@ -54,9 +58,12 @@ class KeyboardKeyEmbedderHandler uint64_t response_id; }; - // Record the last seen physical and logical key for a virtual key. - struct CheckedKey { + // The information for a virtual key that's important enough that its + // state is checked after every event. + struct CriticalKey { // Last seen value of physical key and logical key for the virtual key. + // + // Used to synthesize down events. uint64_t physical_key; uint64_t logical_key; @@ -64,16 +71,24 @@ class KeyboardKeyEmbedderHandler // keys). bool check_pressed; // Whether to ensure the toggled state of the key (usually for lock keys). + // + // If this is true, `check_pressed` must be true. bool check_toggled; // Whether the lock key is currently toggled on. bool toggled_on; }; - void InitCheckedKeys(); + // Assign |critical_keys_| with basic information. + void InitCriticalKeys(); + // Update |critical_keys_| with last seen logical and physical key. void UpdateLastSeenCritialKey(int virtual_key, uint64_t physical_key, uint64_t logical_key); + // Check each key's state from |get_key_state_| and synthesize events + // if their toggling states have been desynchronized. void SynchroizeCritialToggledStates(int this_virtual_key); + // Check each key's state from |get_key_state_| and synthesize events + // if their pressing states have been desynchronized. void SynchroizeCritialPressedStates(); std::function @@ -91,7 +106,7 @@ class KeyboardKeyEmbedderHandler // // The following maps map Win32 virtual key to the physical key and logical // key they're last seen. - std::map critical_keys_; + std::map critical_keys_; static uint64_t getPhysicalKey(int scancode, bool extended); static uint64_t getLogicalKey(int key, bool extended, int scancode); From d4457f2256b72f962796eb611ce0b9a077ee8509 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 17 Feb 2021 17:38:51 -0800 Subject: [PATCH 79/82] Test initial state --- ...keyboard_key_embedder_handler_unittests.cc | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index 58362f3fb4065..57a11e48e9792 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -636,7 +636,7 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledState) { }, key_state.Getter()); - // Test if the app starts with NumLock on + // The NumLock is desynchronized by toggled on key_state.Set(VK_NUMLOCK, false, true); // Send a normal event @@ -801,5 +801,41 @@ TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeForDesyncToggledStateByItself) { EXPECT_EQ(last_handled, true); } +TEST(KeyboardKeyEmbedderHandlerTest, SynthesizeWithInitialTogglingState) { + TestKeystate key_state; + std::vector results; + TestFlutterKeyEvent* event; + bool last_handled = false; + + // The app starts with NumLock toggled on + key_state.Set(VK_NUMLOCK, false, true); + + std::unique_ptr handler = + std::make_unique( + [&results](const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, void* user_data) { + results.emplace_back(event, callback, user_data); + }, + key_state.Getter()); + + // NumLock key down + key_state.Set(VK_NUMLOCK, true, false); + handler->KeyboardHook( + VK_NUMLOCK, kScanCodeNumLock, WM_KEYDOWN, 0, true, false, + [&last_handled](bool handled) { last_handled = handled; }); + EXPECT_EQ(last_handled, false); + EXPECT_EQ(results.size(), 1); + event = &results[0]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalKeyNumLock); + EXPECT_EQ(event->logical, kLogicalKeyNumLock); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, false); + + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); + results.clear(); +} + } // namespace testing } // namespace flutter From 2e962b45b573118e1466ece615a4253468f5551f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 24 Feb 2021 16:26:22 -0800 Subject: [PATCH 80/82] Fix comments --- .../platform/windows/flutter_windows_view.cc | 4 +-- .../windows/keyboard_key_embedder_handler.cc | 18 +++++----- .../windows/keyboard_key_embedder_handler.h | 10 ++++-- shell/platform/windows/keyboard_key_handler.h | 34 ++++++++++++------- .../windows/keyboard_key_handler_unittests.cc | 2 +- 5 files changed, 42 insertions(+), 26 deletions(-) diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 4f108f7f63b2b..93d2aae6346d8 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -76,8 +76,8 @@ void FlutterWindowsView::RegisterKeyboardHandlers( // // Whether an event is a redispatched event is decided by calculating the hash // of the event. In order to allow the same real event in the future, the - // handler is "toggled" when events pass through, which means this logic does - // not apply when there is more than 1 handler. + // handler is "toggled" when events pass through, therefore the redispatching + // algorithm does not allow more than 1 handler that takes |SendInput|. auto key_handler = std::make_unique(SendInput); key_handler->AddDelegate( std::make_unique(messenger)); diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index 39e31267a9686..ad34793ca2b40 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -78,7 +78,7 @@ static bool isControlCharacter(int codeUnit) { // Transform scancodes sent by windows to scancodes written in Chromium spec. static uint16_t normalizeScancode(int windowsScanCode, bool extended) { // In Chromium spec the extended bit is shown as 0xe000 bit, - // i.e. PageUp is represented as 0xe049. + // e.g. PageUp is represented as 0xe049. return (windowsScanCode & 0xff) | (extended ? 0xe000 : 0); } @@ -97,10 +97,10 @@ uint64_t KeyboardKeyEmbedderHandler::getLogicalKey(int key, // Normally logical keys should only be derived from key codes, but since some // key codes are either 0 or ambiguous (multiple keys using the same key // code), these keys are resolved by scan codes. - auto numpadIt = + auto numpadIter = scanCodeToLogicalMap_.find(normalizeScancode(scancode, extended)); - if (numpadIt != scanCodeToLogicalMap_.cend()) - return numpadIt->second; + if (numpadIter != scanCodeToLogicalMap_.cend()) + return numpadIter->second; // Check if the keyCode is one we know about and have a mapping for. auto logicalIt = windowsToLogicalMap_.find(key); @@ -199,7 +199,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( } UpdateLastSeenCritialKey(key, physical_key, result_logical_key); - SynchroizeCritialToggledStates(type == kFlutterKeyEventTypeDown ? key : 0); + SynchronizeCritialToggledStates(type == kFlutterKeyEventTypeDown ? key : 0); if (next_has_record) { pressingRecords_[physical_key] = next_logical_record; @@ -207,7 +207,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHook( pressingRecords_.erase(last_logical_record_iter); } - SynchroizeCritialPressedStates(); + SynchronizeCritialPressedStates(); if (result_logical_key == VK_PROCESSKEY) { // VK_PROCESSKEY means that the key press is used by an IME. These key @@ -262,7 +262,7 @@ void KeyboardKeyEmbedderHandler::UpdateLastSeenCritialKey( } } -void KeyboardKeyEmbedderHandler::SynchroizeCritialToggledStates( +void KeyboardKeyEmbedderHandler::SynchronizeCritialToggledStates( int toggle_virtual_key) { for (auto& kv : critical_keys_) { UINT virtual_key = kv.first; @@ -303,7 +303,7 @@ void KeyboardKeyEmbedderHandler::SynchroizeCritialToggledStates( } } -void KeyboardKeyEmbedderHandler::SynchroizeCritialPressedStates() { +void KeyboardKeyEmbedderHandler::SynchronizeCritialPressedStates() { for (auto& kv : critical_keys_) { UINT virtual_key = kv.first; CriticalKey& key_info = kv.second; @@ -357,6 +357,8 @@ void KeyboardKeyEmbedderHandler::InitCriticalKeys() { }; }; + // TODO(dkwingsmt): Consider adding more critical keys here. + // https://github.com/flutter/flutter/issues/76736 critical_keys_.emplace(VK_LSHIFT, createCheckedKey(VK_LSHIFT, false, true, false)); critical_keys_.emplace(VK_RSHIFT, diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index efcb7cdb4a9e6..3dc875349856c 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -34,7 +34,7 @@ class KeyboardKeyEmbedderHandler // Use `send_event` to define how the class should dispatch converted // flutter events, as well as how to receive the response, to the engine. It's // typically FlutterWindowsEngine::SendKeyEvent. The 2nd and 3rd parameter - // of the call might be nullptr. + // of the SendEvent call might be nullptr. // // Use `get_key_state` to define how the class should get a reliable result of // the state for a virtual key. It's typically Win32's GetKeyState. @@ -86,10 +86,10 @@ class KeyboardKeyEmbedderHandler uint64_t logical_key); // Check each key's state from |get_key_state_| and synthesize events // if their toggling states have been desynchronized. - void SynchroizeCritialToggledStates(int this_virtual_key); + void SynchronizeCritialToggledStates(int this_virtual_key); // Check each key's state from |get_key_state_| and synthesize events // if their pressing states have been desynchronized. - void SynchroizeCritialPressedStates(); + void SynchronizeCritialPressedStates(); std::function sendEvent_; @@ -98,7 +98,11 @@ class KeyboardKeyEmbedderHandler // A map from physical keys to logical keys, each entry indicating a pressed // key. std::map pressingRecords_; + // Information for key events that have been sent to the framework but yet + // to receive the response. Indexed by response IDs. std::map> pending_responses_; + // A self-incrementing integer, used as the ID for the next entry for + // |pending_responses_|. uint64_t response_id_; // Important keys whose states are checked and guaranteed synchronized diff --git a/shell/platform/windows/keyboard_key_handler.h b/shell/platform/windows/keyboard_key_handler.h index f7f41e892a9b6..b7856b55e0571 100644 --- a/shell/platform/windows/keyboard_key_handler.h +++ b/shell/platform/windows/keyboard_key_handler.h @@ -26,8 +26,12 @@ class FlutterWindowsView; // and redispatches events unhandled by Flutter back to the system. // See |KeyboardHook| for more information about dispatching. // -// The exact behavior to handle events are further forwarded into -// delegates. See |KeyboardKeyHandlerDelegate| and its subclasses. +// This class owns multiple |KeyboardKeyHandlerDelegate|s, which +// implements the exact behavior to asynchronously handle events. In +// reality, this design is only to support sending events through +// "channel" (RawKeyEvent) and "embedder" (KeyEvent) simultaneously, +// the former of which shall be removed after the deprecation window +// of the RawKeyEvent system. class KeyboardKeyHandler : public KeyboardHandlerBase { public: // An interface for concrete definition of how to asynchronously handle key @@ -65,20 +69,26 @@ class KeyboardKeyHandler : public KeyboardHandlerBase { // Handles a key event. // // Returns whether this handler claims to handle the event, which is true if - // the event is a native event, or false if the event is a redispatched one. + // and only if the event is a non-synthesized event. // // Windows requires a synchronous response of whether a key event should be // handled, while the query to Flutter is always asynchronous. This is - // resolved by "redispatching": the response to the native event is always - // true. If Flutter later decides not to handle the event, an event is then - // synthesized, dispatched to system, received again, detected, at which time - // |KeyboardHook| returns false, then falls back to other keyboard handlers. + // resolved by the "redispatching" algorithm: by default, the response to a + // fresh event is always always true. The event is then sent to the framework. + // If the framework later decides not to handle the event, this class will + // create an identical event and dispatch it to the system, and remember all + // synthesized events. The fist time an exact event (by |ComputeEventHash|) is + // received in the future, the new event is considered a synthesized one, + // causing |KeyboardHook| to return false to fall back to other keyboard + // handlers. // - // Received events are further dispatched to all added delegates. If any - // delegate returns true (handled), the event is considered handled. When - // all delegates responded, any unhandled events are redispatched via - // |redispatch_event| and recorded. The next (one) time this exact event is - // received, |KeyboardHook| will skip it and immediately return false. + // Whether a non-synthesized event is considered handled by the framework is + // decided by dispatching the event to all delegates, simultaneously, + // unconditionally, in insertion order, and collecting their responses later. + // It's not supported to prevent any delegates to process the events, because + // in reality this will only support 2 hardcoded delegates, and only to + // continut supporting the legacy API (channel) during the deprecation window, + // after which the channel delegate should be removed. // // Inherited from |KeyboardHandlerBase|. bool KeyboardHook(FlutterWindowsView* window, diff --git a/shell/platform/windows/keyboard_key_handler_unittests.cc b/shell/platform/windows/keyboard_key_handler_unittests.cc index 267c1d0dff440..22e6244b3f0fb 100644 --- a/shell/platform/windows/keyboard_key_handler_unittests.cc +++ b/shell/platform/windows/keyboard_key_handler_unittests.cc @@ -343,7 +343,7 @@ TEST(KeyboardKeyHandlerTest, WithTwoAsyncDelegates) { // Regression test for a crash in an earlier implementation. // -// In real life, the framework responses slowly. The next real event might +// In real life, the framework responds slowly. The next real event might // arrive earlier than the framework response, and if the 2nd event is identical // to the one waiting for response, an earlier implementation will crash upon // the response. From 01223a3613c5fc80afe407a53df85b8cad1e8b6f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 26 Feb 2021 14:19:37 -0800 Subject: [PATCH 81/82] Fix arg name --- .../windows/win32_flutter_window_unittests.cc | 16 ++++++++-------- shell/platform/windows/win32_window_unittests.cc | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/shell/platform/windows/win32_flutter_window_unittests.cc b/shell/platform/windows/win32_flutter_window_unittests.cc index 6fb3afcd26f0a..39ed090049c25 100644 --- a/shell/platform/windows/win32_flutter_window_unittests.cc +++ b/shell/platform/windows/win32_flutter_window_unittests.cc @@ -28,15 +28,15 @@ namespace testing { namespace { // Creates a valid Windows LPARAM for WM_KEYDOWN and WM_CHAR from parameters // given. -static LPARAM CreateKeyEventLparam(USHORT ScanCode, +static LPARAM CreateKeyEventLparam(USHORT scancode, bool extended = false, - bool PreviousKeyState = 1, - USHORT RepeatCount = 1, - bool ContextCode = 0, - bool TransitionState = 1) { - return ((LPARAM(TransitionState) << 31) | (LPARAM(PreviousKeyState) << 30) | - (LPARAM(ContextCode) << 29) | (LPARAM(extended ? 0x1 : 0x0) << 24) | - (LPARAM(ScanCode) << 16) | LPARAM(RepeatCount)); + bool was_down = 1, + USHORT repeat_count = 1, + bool context_code = 0, + bool transition_state = 1) { + return ((LPARAM(transition_state) << 31) | (LPARAM(was_down) << 30) | + (LPARAM(context_code) << 29) | (LPARAM(extended ? 0x1 : 0x0) << 24) | + (LPARAM(scancode) << 16) | LPARAM(repeat_count)); } // A struc to hold simulated events that will be delivered after the framework diff --git a/shell/platform/windows/win32_window_unittests.cc b/shell/platform/windows/win32_window_unittests.cc index 38a1b068fd55d..53b2a1444bc38 100644 --- a/shell/platform/windows/win32_window_unittests.cc +++ b/shell/platform/windows/win32_window_unittests.cc @@ -13,15 +13,15 @@ namespace { // Creates a valid Windows LPARAM for WM_KEYDOWN and WM_KEYUP from parameters // given. -static LPARAM CreateKeyEventLparam(USHORT ScanCode, +static LPARAM CreateKeyEventLparam(USHORT scancode, bool extended = false, - bool PreviousKeyState = 1, - USHORT RepeatCount = 1, - bool ContextCode = 0, - bool TransitionState = 1) { - return ((LPARAM(TransitionState) << 31) | (LPARAM(PreviousKeyState) << 30) | - (LPARAM(ContextCode) << 29) | (LPARAM(extended ? 0x1 : 0x0) << 24) | - (LPARAM(ScanCode) << 16) | LPARAM(RepeatCount)); + bool was_down = 1, + USHORT repeat_count = 1, + bool context_code = 0, + bool transition_state = 1) { + return ((LPARAM(transition_state) << 31) | (LPARAM(was_down) << 30) | + (LPARAM(context_code) << 29) | (LPARAM(extended ? 0x1 : 0x0) << 24) | + (LPARAM(ScanCode) << 16) | LPARAM(repeat_count)); } } // namespace From c7c8fca1b2cda58466732932d80909c92125ed01 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 26 Feb 2021 14:37:58 -0800 Subject: [PATCH 82/82] Fix compile --- shell/platform/windows/win32_window_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/windows/win32_window_unittests.cc b/shell/platform/windows/win32_window_unittests.cc index 53b2a1444bc38..b9297528096f7 100644 --- a/shell/platform/windows/win32_window_unittests.cc +++ b/shell/platform/windows/win32_window_unittests.cc @@ -21,7 +21,7 @@ static LPARAM CreateKeyEventLparam(USHORT scancode, bool transition_state = 1) { return ((LPARAM(transition_state) << 31) | (LPARAM(was_down) << 30) | (LPARAM(context_code) << 29) | (LPARAM(extended ? 0x1 : 0x0) << 24) | - (LPARAM(ScanCode) << 16) | LPARAM(repeat_count)); + (LPARAM(scancode) << 16) | LPARAM(repeat_count)); } } // namespace