From e73b58f946942a200875b71d67d8d9dfdd1067bc Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 14 Jul 2021 04:05:50 -0700 Subject: [PATCH 1/7] Impl --- shell/platform/windows/window_win32.cc | 22 ++++++++++------------ shell/platform/windows/window_win32.h | 4 ++++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/shell/platform/windows/window_win32.cc b/shell/platform/windows/window_win32.cc index f827acc0e8344..882c3717a0895 100644 --- a/shell/platform/windows/window_win32.cc +++ b/shell/platform/windows/window_win32.cc @@ -329,10 +329,10 @@ WindowWin32::HandleMessage(UINT const message, s_pending_high_surrogate = 0; } - // All key presses that generate a character should be sent from + // Key presses that generate a non-surrogate should be sent from // 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) { + if (keycode_for_char_message_ != 0 && (IS_HIGH_SURROGATE(character) || IS_LOW_SURROGATE(character))) { const unsigned int scancode = (lparam >> 16) & 0xff; const bool extended = ((lparam >> 24) & 0x01) == 0x01; const bool was_down = lparam & 0x40000000; @@ -356,7 +356,7 @@ WindowWin32::HandleMessage(UINT const message, // - Lead surrogates, which like dead keys will be send once combined. // - ASCII control characters, which are sent as WM_CHAR events for all // control key shortcuts. - if (message == WM_CHAR && s_pending_high_surrogate == 0 && + if (!handled_for_char_message_ && message == WM_CHAR && s_pending_high_surrogate == 0 && character >= u' ') { OnText(text); } @@ -366,19 +366,16 @@ WindowWin32::HandleMessage(UINT const message, case WM_SYSKEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: + handled_for_char_message_ = false; const bool is_keydown_message = (message == WM_KEYDOWN || message == WM_SYSKEYDOWN); - // Check if this key produces a character. If so, the key press should - // be sent with the character produced at WM_CHAR. Store the produced + // Check if this key produces a surrogate. If so, the key press should + // be resolved with the character produced at WM_CHAR. Store the produced // keycode (it's not accessible from WM_CHAR) to be used in WM_CHAR. - // - // Messages with Control or Win modifiers down are never considered as - // character messages. This allows key combinations such as "CTRL + Digit" - // to properly produce key down events even though `MapVirtualKey` returns - // a valid character. See https://github.com/flutter/flutter/issues/85587. unsigned int character = MapVirtualKey(wparam, MAPVK_VK_TO_CHAR); - if (character > 0 && is_keydown_message && GetKeyState(VK_CONTROL) == 0 && - GetKeyState(VK_LWIN) == 0 && GetKeyState(VK_RWIN) == 0) { + keycode_for_char_message_ = 0; + if (character > 0 && is_keydown_message && + (IS_HIGH_SURROGATE(character) || IS_LOW_SURROGATE(character))) { keycode_for_char_message_ = wparam; break; } @@ -392,6 +389,7 @@ WindowWin32::HandleMessage(UINT const message, const int action = is_keydown_message ? WM_KEYDOWN : WM_KEYUP; const bool was_down = lparam & 0x40000000; if (OnKey(keyCode, scancode, action, 0, extended, was_down)) { + handled_for_char_message_ = true; return 0; } break; diff --git a/shell/platform/windows/window_win32.h b/shell/platform/windows/window_win32.h index 37d8d9aa18719..7ac8ffb21fd00 100644 --- a/shell/platform/windows/window_win32.h +++ b/shell/platform/windows/window_win32.h @@ -194,6 +194,10 @@ class WindowWin32 { // message. int keycode_for_char_message_ = 0; + // Keeps track of the event result produced by a WM_KEYDOWN or WM_SYSKEYDOWN + // message. + bool handled_for_char_message_ = false; + // Manages IME state. TextInputManagerWin32 text_input_manager_; }; From 986535f3c9531429501fd57bc0539f8089a29504 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 3 Aug 2021 04:09:43 -0700 Subject: [PATCH 2/7] Fix tests --- .../windows/flutter_window_win32_unittests.cc | 106 ++++++++++-------- shell/platform/windows/window_win32.cc | 2 +- .../windows/window_win32_unittests.cc | 16 +-- 3 files changed, 70 insertions(+), 54 deletions(-) diff --git a/shell/platform/windows/flutter_window_win32_unittests.cc b/shell/platform/windows/flutter_window_win32_unittests.cc index 7f01ed5c9930e..cf83d666f7469 100644 --- a/shell/platform/windows/flutter_window_win32_unittests.cc +++ b/shell/platform/windows/flutter_window_win32_unittests.cc @@ -393,7 +393,7 @@ TEST(FlutterWindowWin32Test, NonPrintableKeyDownPropagation) { // Tests key event propagation of printable character key down events. These // 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(FlutterWindowWin32Test, CharKeyDownPropagation) { +TEST(FlutterWindowWin32Test, CharKeyDownPropagationNotHandled) { // ::testing::InSequence in_sequence; constexpr WPARAM virtual_key = 65; // The "A" key, which produces a character @@ -409,52 +409,66 @@ TEST(FlutterWindowWin32Test, CharKeyDownPropagation) { LPARAM lparam = CreateKeyEventLparam(scan_code, false /* extended */, true /* PrevState */); - // Test an event not handled by the framework - { - 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, true)) - .Times(2) - .RetiresOnSaturation(); - EXPECT_CALL(*flutter_windows_view.text_input_plugin, - KeyboardHook(_, _, _, _, _, _, _)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*flutter_windows_view.key_event_handler, TextHook(_, _)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*flutter_windows_view.text_input_plugin, TextHook(_, _)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_EQ(win32window.InjectWindowMessage(WM_KEYDOWN, virtual_key, lparam), - 0); - EXPECT_EQ(win32window.InjectWindowMessage(WM_CHAR, virtual_key, lparam), 0); - flutter_windows_view.InjectPendingEvents(&win32window); - } - return; + 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, true)) + .Times(2) + .RetiresOnSaturation(); + EXPECT_CALL(*flutter_windows_view.text_input_plugin, + KeyboardHook(_, _, _, _, _, _, _)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*flutter_windows_view.key_event_handler, TextHook(_, _)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*flutter_windows_view.text_input_plugin, TextHook(_, _)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_EQ(win32window.InjectWindowMessage(WM_KEYDOWN, virtual_key, lparam), + 0); + EXPECT_EQ(win32window.InjectWindowMessage(WM_CHAR, virtual_key, lparam), 0); + flutter_windows_view.InjectPendingEvents(&win32window); +} - // 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 */, true)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*flutter_windows_view.text_input_plugin, - KeyboardHook(_, _, _, _, _, _, _)) - .Times(0); - EXPECT_CALL(*flutter_windows_view.key_event_handler, TextHook(_, _)) - .Times(0); - EXPECT_CALL(*flutter_windows_view.text_input_plugin, TextHook(_, _)) - .Times(0); - EXPECT_EQ(win32window.InjectWindowMessage(WM_KEYDOWN, virtual_key, lparam), - 0); - EXPECT_EQ(win32window.InjectWindowMessage(WM_CHAR, virtual_key, lparam), 0); - flutter_windows_view.InjectPendingEvents(&win32window); - } +// Tests key event propagation of printable character key down events. These +// 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(FlutterWindowWin32Test, CharKeyDownPropagationHandled) { + // ::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; + + MockFlutterWindowWin32 win32window; + auto window_binding_handler = + std::make_unique<::testing::NiceMock>(); + 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 */); + + test_response = true; + flutter_windows_view.SetEngine(std::move(GetTestEngine())); + EXPECT_CALL(*flutter_windows_view.key_event_handler, + KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, + false /* is_printable */, true)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*flutter_windows_view.text_input_plugin, + KeyboardHook(_, _, _, _, _, _, _)) + .Times(0); + EXPECT_CALL(*flutter_windows_view.key_event_handler, TextHook(_, _)) + .Times(0); + EXPECT_CALL(*flutter_windows_view.text_input_plugin, TextHook(_, _)) + .Times(0); + EXPECT_EQ(win32window.InjectWindowMessage(WM_KEYDOWN, virtual_key, lparam), + 0); + EXPECT_EQ(win32window.InjectWindowMessage(WM_CHAR, virtual_key, lparam), 0); + flutter_windows_view.InjectPendingEvents(&win32window); } // Tests key event propagation of modifier key down events. This are different diff --git a/shell/platform/windows/window_win32.cc b/shell/platform/windows/window_win32.cc index 882c3717a0895..4d4b9ef096551 100644 --- a/shell/platform/windows/window_win32.cc +++ b/shell/platform/windows/window_win32.cc @@ -388,7 +388,7 @@ WindowWin32::HandleMessage(UINT const message, } const int action = is_keydown_message ? WM_KEYDOWN : WM_KEYUP; const bool was_down = lparam & 0x40000000; - if (OnKey(keyCode, scancode, action, 0, extended, was_down)) { + if (OnKey(keyCode, scancode, action, character, extended, was_down)) { handled_for_char_message_ = true; return 0; } diff --git a/shell/platform/windows/window_win32_unittests.cc b/shell/platform/windows/window_win32_unittests.cc index ce54155cd7a96..9947a2c9efc6d 100644 --- a/shell/platform/windows/window_win32_unittests.cc +++ b/shell/platform/windows/window_win32_unittests.cc @@ -74,12 +74,13 @@ TEST(MockWin32Window, KeyUp) { 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, true)).Times(0); + // OnKey shoulde called during the WM_KEYDOWN message. + EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false, true)).Times(1); // send a "A" key down event. window.InjectWindowMessage(WM_KEYDOWN, 65, lparam); - EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false, true)).Times(1); + // OnKey shouldn't be called during the WM_CHAR message. + EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false, true)).Times(0); EXPECT_CALL(window, OnText(_)).Times(1); window.InjectWindowMessage(WM_CHAR, 65, lparam); } @@ -97,7 +98,7 @@ TEST(MockWin32Window, KeyDownWithCtrl) { // Expect OnKey, but not OnText, because Control + Key is not followed by // WM_CHAR - EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 0, false, true)).Times(1); + EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 'A', false, true)).Times(1); EXPECT_CALL(window, OnText(_)).Times(0); window.InjectWindowMessage(WM_KEYDOWN, 65, lparam); @@ -117,12 +118,13 @@ TEST(MockWin32Window, KeyDownWithCtrlToggled) { LPARAM lparam = CreateKeyEventLparam(30); - // OnKey shouldn't be called until the WM_CHAR message. - EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false, true)).Times(0); + // OnKey should be called during the WM_KEYDOWN message. + EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false, true)).Times(1); // send a "A" key down event. window.InjectWindowMessage(WM_KEYDOWN, 65, lparam); - EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false, true)).Times(1); + // OnKey shouldn't be called during the WM_CHAR message. + EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 65, false, true)).Times(0); EXPECT_CALL(window, OnText(_)).Times(1); window.InjectWindowMessage(WM_CHAR, 65, lparam); From d787ae1558e354f34e4205fbc3b6a7da9e0c5b06 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 4 Aug 2021 03:06:48 -0700 Subject: [PATCH 3/7] ResolveKeyCode --- shell/platform/windows/window_win32.cc | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/shell/platform/windows/window_win32.cc b/shell/platform/windows/window_win32.cc index 4d4b9ef096551..c8999a60576a8 100644 --- a/shell/platform/windows/window_win32.cc +++ b/shell/platform/windows/window_win32.cc @@ -172,6 +172,22 @@ void WindowWin32::UpdateCursorRect(const Rect& rect) { text_input_manager_.UpdateCaretRect(rect); } +static uint16_t ResolveKeyCode(uint16_t original, bool extended, uint8_t scancode) { + switch (original) { + case VK_SHIFT: + case VK_LSHIFT: + return MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX);; + case VK_MENU: + case VK_LMENU: + return extended ? VK_RMENU : VK_LMENU; + case VK_CONTROL: + case VK_LCONTROL: + return extended ? VK_RCONTROL : VK_LCONTROL; + default: + return original; + } +} + LRESULT WindowWin32::HandleMessage(UINT const message, WPARAM const wparam, @@ -383,9 +399,7 @@ WindowWin32::HandleMessage(UINT const message, const unsigned int scancode = (lparam >> 16) & 0xff; const bool extended = ((lparam >> 24) & 0x01) == 0x01; // 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); - } + keyCode = ResolveKeyCode(keyCode, extended, scancode); const int action = is_keydown_message ? WM_KEYDOWN : WM_KEYUP; const bool was_down = lparam & 0x40000000; if (OnKey(keyCode, scancode, action, character, extended, was_down)) { From 59bb9bfc2d5a0870764f42a1251b09647f72f13e Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 4 Aug 2021 05:48:59 -0700 Subject: [PATCH 4/7] Fix AltGr --- lib/ui/key.dart | 2 +- shell/platform/windows/keyboard_key_embedder_handler.cc | 4 ++-- shell/platform/windows/keyboard_key_handler.cc | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/ui/key.dart b/lib/ui/key.dart index 50e07703fb977..06ccf66de4165 100644 --- a/lib/ui/key.dart +++ b/lib/ui/key.dart @@ -151,7 +151,7 @@ class KeyData { @override String toString() => 'KeyData(key ${_typeToString(type)}, physical: 0x${physical.toRadixString(16)}, ' - 'logical: ${_logicalToString()}, character: ${_escapeCharacter()})'; + 'logical: ${_logicalToString()}, character: ${_escapeCharacter()}${synthesized ? ', synthesized' : ''})'; /// Returns a complete textual description of the information in this object. String toStringFull() { diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index b1aa06d1bbddb..1c4bf4d2037ae 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -267,10 +267,10 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialToggledStates( continue; } assert(key_info.logical_key != 0); - SHORT state = get_key_state_(virtual_key); // Check toggling state first, because it might alter pressing state. if (key_info.check_toggled) { + SHORT state = get_key_state_(virtual_key); bool should_toggled = state & kStateMaskToggled; if (virtual_key == toggle_virtual_key) { key_info.toggled_on = !key_info.toggled_on; @@ -312,8 +312,8 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialPressedStates() { continue; } assert(key_info.logical_key != 0); - SHORT state = get_key_state_(virtual_key); if (key_info.check_pressed) { + SHORT state = get_key_state_(virtual_key); auto recorded_pressed_iter = pressingRecords_.find(key_info.physical_key); bool recorded_pressed = recorded_pressed_iter != pressingRecords_.end(); bool should_pressed = state & kStateMaskPressed; diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index 4264039d658aa..0f80687cbba73 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -62,7 +62,7 @@ static bool IsKeyDownAltRight(int action, int virtual_key, bool extended) { #ifdef WINUWP return false; #else - return virtual_key == VK_LMENU && extended && action == WM_KEYDOWN; + return virtual_key == VK_RMENU && extended && action == WM_KEYDOWN; #endif } @@ -73,7 +73,7 @@ static bool IsKeyUpAltRight(int action, int virtual_key, bool extended) { #ifdef WINUWP return false; #else - return virtual_key == VK_LMENU && extended && action == WM_KEYUP; + return virtual_key == VK_RMENU && extended && action == WM_KEYUP; #endif } From 19b7c34d66b64006c87e5e2b36fe3a92bda971ba Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 4 Aug 2021 18:04:44 -0700 Subject: [PATCH 5/7] Fix dead key --- lib/ui/key.dart | 10 +++++++++- shell/platform/windows/keyboard_key_handler.cc | 8 ++++++-- shell/platform/windows/window_win32.cc | 5 +++++ shell/platform/windows/window_win32.h | 2 ++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lib/ui/key.dart b/lib/ui/key.dart index 06ccf66de4165..ac6b8b7755a90 100644 --- a/lib/ui/key.dart +++ b/lib/ui/key.dart @@ -149,9 +149,17 @@ class KeyData { } } + String? _quotedCharCode() { + if (character == null) + return ''; + final Iterable hexChars = character!.codeUnits + .map((int code) => code.toRadixString(16).padLeft(2, '0')); + return '0x${hexChars.join(' ')}'; + } + @override String toString() => 'KeyData(key ${_typeToString(type)}, physical: 0x${physical.toRadixString(16)}, ' - 'logical: ${_logicalToString()}, character: ${_escapeCharacter()}${synthesized ? ', synthesized' : ''})'; + 'logical: ${_logicalToString()}, character: ${_escapeCharacter()}${_quotedCharCode()}${synthesized ? ', synthesized' : ''})'; /// Returns a complete textual description of the information in this object. String toStringFull() { diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index 0f80687cbba73..b6180fc508e65 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -220,8 +220,10 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, } pending_responds_.push_back(std::move(incoming)); + const bool is_deadchar = character & 0x80000000; + const uint32_t key_character = is_deadchar ? 0 : character; for (const auto& delegate : delegates_) { - delegate->KeyboardHook(key, scancode, action, character, extended, was_down, + delegate->KeyboardHook(key, scancode, action, key_character, extended, was_down, [sequence_id, this](bool handled) { ResolvePendingEvent(sequence_id, handled); }); @@ -261,7 +263,9 @@ void KeyboardKeyHandler::ResolvePendingEvent(uint64_t sequence_id, if (event.unreplied == 0) { std::unique_ptr event_ptr = std::move(*iter); pending_responds_.erase(iter); - if (!event_ptr->any_handled) { + const bool is_deadchar = event.character & 0x80000000; + const bool should_redispatch = !event_ptr->any_handled && !is_deadchar; + if (should_redispatch) { RedispatchEvent(std::move(event_ptr)); } } diff --git a/shell/platform/windows/window_win32.cc b/shell/platform/windows/window_win32.cc index c8999a60576a8..06fd980a19378 100644 --- a/shell/platform/windows/window_win32.cc +++ b/shell/platform/windows/window_win32.cc @@ -329,6 +329,9 @@ WindowWin32::HandleMessage(UINT const message, case WM_SYSDEADCHAR: case WM_CHAR: case WM_SYSCHAR: { + if (ignore_next_event) { + break; + } static wchar_t s_pending_high_surrogate = 0; wchar_t character = static_cast(wparam); @@ -405,6 +408,8 @@ WindowWin32::HandleMessage(UINT const message, if (OnKey(keyCode, scancode, action, character, extended, was_down)) { handled_for_char_message_ = true; return 0; + } else { + ignore_next_event = true; } break; } diff --git a/shell/platform/windows/window_win32.h b/shell/platform/windows/window_win32.h index 7ac8ffb21fd00..ca7a975e0ffbc 100644 --- a/shell/platform/windows/window_win32.h +++ b/shell/platform/windows/window_win32.h @@ -198,6 +198,8 @@ class WindowWin32 { // message. bool handled_for_char_message_ = false; + bool ignore_next_event = false; + // Manages IME state. TextInputManagerWin32 text_input_manager_; }; From 780d4df9dc8d2bcd9318fd739046fa312c6916b1 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 5 Aug 2021 16:09:44 -0700 Subject: [PATCH 6/7] keyboard_test --- shell/platform/windows/BUILD.gn | 3 + .../windows/flutter_window_win32_unittests.cc | 128 +++--- .../platform/windows/flutter_windows_view.cc | 26 +- shell/platform/windows/flutter_windows_view.h | 7 +- .../platform/windows/keyboard_key_handler.cc | 2 +- shell/platform/windows/keyboard_unittests.cc | 384 ++++++++++++++++++ .../platform/windows/testing/test_keyboard.cc | 21 + .../platform/windows/testing/test_keyboard.h | 65 +++ shell/platform/windows/window_win32.cc | 2 +- 9 files changed, 553 insertions(+), 85 deletions(-) create mode 100644 shell/platform/windows/keyboard_unittests.cc create mode 100644 shell/platform/windows/testing/test_keyboard.cc create mode 100644 shell/platform/windows/testing/test_keyboard.h diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 4ed1b0a8e7cfe..8936d1cd32caa 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -240,6 +240,7 @@ executable("flutter_windows_unittests") { "flutter_windows_engine_unittests.cc", "flutter_windows_texture_registrar_unittests.cc", "flutter_windows_view_unittests.cc", + "keyboard_unittests.cc", "keyboard_key_channel_handler_unittests.cc", "keyboard_key_embedder_handler_unittests.cc", "keyboard_key_handler_unittests.cc", @@ -249,6 +250,8 @@ executable("flutter_windows_unittests") { "testing/mock_window_binding_handler.h", "testing/mock_window_win32.cc", "testing/mock_window_win32.h", + "testing/test_keyboard.cc", + "testing/test_keyboard.h", "text_input_plugin_unittest.cc", "window_proc_delegate_manager_win32_unittests.cc", "window_win32_unittests.cc", diff --git a/shell/platform/windows/flutter_window_win32_unittests.cc b/shell/platform/windows/flutter_window_win32_unittests.cc index cf83d666f7469..1435afb13b409 100644 --- a/shell/platform/windows/flutter_window_win32_unittests.cc +++ b/shell/platform/windows/flutter_window_win32_unittests.cc @@ -30,8 +30,8 @@ namespace { // Creates a valid Windows LPARAM for WM_KEYDOWN and WM_CHAR from parameters // given. static LPARAM CreateKeyEventLparam(USHORT scancode, - bool extended = false, - bool was_down = 1, + bool extended, + bool was_down, USHORT repeat_count = 1, bool context_code = 0, bool transition_state = 1) { @@ -215,7 +215,10 @@ class TestFlutterWindowsView : public FlutterWindowsView { } protected: - void RegisterKeyboardHandlers(flutter::BinaryMessenger* messenger) override { + void RegisterKeyboardHandlers( + flutter::BinaryMessenger* messenger, + flutter::KeyboardKeyHandler::EventDispatcher dispatch_event, + flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state) override { auto spy_key_event_handler = std::make_unique( messenger, [this](UINT cInputs, LPINPUT pInputs, int cbSize) -> UINT { return this->SendInput(cInputs, pInputs, cbSize); @@ -235,8 +238,9 @@ class TestFlutterWindowsView : public FlutterWindowsView { const KEYBDINPUT kbdinput = pInputs->ki; const UINT message = (kbdinput.dwFlags & KEYEVENTF_KEYUP) ? WM_KEYUP : WM_KEYDOWN; + const bool is_key_up = kbdinput.dwFlags & KEYEVENTF_KEYUP; const LPARAM lparam = CreateKeyEventLparam( - kbdinput.wScan, kbdinput.dwFlags & KEYEVENTF_EXTENDEDKEY); + kbdinput.wScan, kbdinput.dwFlags & KEYEVENTF_EXTENDEDKEY, is_key_up); // Windows would normally fill in the virtual key code for us, so we // 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 @@ -345,8 +349,7 @@ TEST(FlutterWindowWin32Test, 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, false); // Test an event not handled by the framework { @@ -393,7 +396,7 @@ TEST(FlutterWindowWin32Test, NonPrintableKeyDownPropagation) { // Tests key event propagation of printable character key down events. These // 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(FlutterWindowWin32Test, CharKeyDownPropagationNotHandled) { +TEST(FlutterWindowWin32Test, CharKeyDownPropagation) { // ::testing::InSequence in_sequence; constexpr WPARAM virtual_key = 65; // The "A" key, which produces a character @@ -406,69 +409,54 @@ TEST(FlutterWindowWin32Test, CharKeyDownPropagationNotHandled) { 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 */); - - test_response = false; + LPARAM lparam = CreateKeyEventLparam(scan_code, false, 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, true)) - .Times(2) - .RetiresOnSaturation(); - EXPECT_CALL(*flutter_windows_view.text_input_plugin, - KeyboardHook(_, _, _, _, _, _, _)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*flutter_windows_view.key_event_handler, TextHook(_, _)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*flutter_windows_view.text_input_plugin, TextHook(_, _)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_EQ(win32window.InjectWindowMessage(WM_KEYDOWN, virtual_key, lparam), - 0); - EXPECT_EQ(win32window.InjectWindowMessage(WM_CHAR, virtual_key, lparam), 0); - flutter_windows_view.InjectPendingEvents(&win32window); -} - -// Tests key event propagation of printable character key down events. These -// 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(FlutterWindowWin32Test, CharKeyDownPropagationHandled) { - // ::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; - - MockFlutterWindowWin32 win32window; - auto window_binding_handler = - std::make_unique<::testing::NiceMock>(); - 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 */); + // Test an event not handled by the framework + { + test_response = 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, + KeyboardHook(_, _, _, _, _, _, _)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*flutter_windows_view.key_event_handler, TextHook(_, _)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*flutter_windows_view.text_input_plugin, TextHook(_, _)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_EQ(win32window.InjectWindowMessage(WM_KEYDOWN, virtual_key, lparam), + 0); + EXPECT_EQ(win32window.InjectWindowMessage(WM_CHAR, virtual_key, lparam), 0); + flutter_windows_view.InjectPendingEvents(&win32window); + } - test_response = true; - flutter_windows_view.SetEngine(std::move(GetTestEngine())); - EXPECT_CALL(*flutter_windows_view.key_event_handler, - KeyboardHook(_, virtual_key, scan_code, WM_KEYDOWN, character, - false /* is_printable */, true)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*flutter_windows_view.text_input_plugin, - KeyboardHook(_, _, _, _, _, _, _)) - .Times(0); - EXPECT_CALL(*flutter_windows_view.key_event_handler, TextHook(_, _)) - .Times(0); - EXPECT_CALL(*flutter_windows_view.text_input_plugin, TextHook(_, _)) - .Times(0); - EXPECT_EQ(win32window.InjectWindowMessage(WM_KEYDOWN, virtual_key, lparam), - 0); - EXPECT_EQ(win32window.InjectWindowMessage(WM_CHAR, virtual_key, lparam), 0); - flutter_windows_view.InjectPendingEvents(&win32window); + // 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, false)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*flutter_windows_view.text_input_plugin, + KeyboardHook(_, _, _, _, _, _, _)) + .Times(0); + EXPECT_CALL(*flutter_windows_view.key_event_handler, TextHook(_, _)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*flutter_windows_view.text_input_plugin, TextHook(_, _)) + .Times(0); + EXPECT_EQ(win32window.InjectWindowMessage(WM_KEYDOWN, virtual_key, lparam), + 0); + EXPECT_EQ(win32window.InjectWindowMessage(WM_CHAR, virtual_key, lparam), 0); + flutter_windows_view.InjectPendingEvents(&win32window); + } } // Tests key event propagation of modifier key down events. This are different @@ -485,7 +473,7 @@ TEST(FlutterWindowWin32Test, ModifierKeyDownPropagation) { 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, false); // Test an event not handled by the framework { @@ -493,7 +481,7 @@ TEST(FlutterWindowWin32Test, 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 */, true)) + false /* extended */, false)) .Times(2) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.text_input_plugin, @@ -514,7 +502,7 @@ TEST(FlutterWindowWin32Test, ModifierKeyDownPropagation) { 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)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*flutter_windows_view.text_input_plugin, diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 55cbc43299dce..356a590e967f0 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -55,7 +55,16 @@ void FlutterWindowsView::SetEngine( // Set up the system channel handlers. auto internal_plugin_messenger = internal_plugin_registrar_->messenger(); - RegisterKeyboardHandlers(internal_plugin_messenger); +#ifdef WINUWP + flutter::KeyboardKeyHandler::EventDispatcher dispatch_event = nullptr; + flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = + nullptr; +#else + flutter::KeyboardKeyHandler::EventDispatcher dispatch_event = SendInput; + flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = + GetKeyState; +#endif + RegisterKeyboardHandlers(internal_plugin_messenger, dispatch_event, get_key_state); platform_handler_ = PlatformHandler::Create(internal_plugin_messenger, this); cursor_handler_ = std::make_unique( internal_plugin_messenger, binding_handler_.get()); @@ -67,7 +76,9 @@ void FlutterWindowsView::SetEngine( } void FlutterWindowsView::RegisterKeyboardHandlers( - flutter::BinaryMessenger* messenger) { + flutter::BinaryMessenger* messenger, + flutter::KeyboardKeyHandler::EventDispatcher dispatch_event, + flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state) { // 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.) @@ -76,17 +87,8 @@ void FlutterWindowsView::RegisterKeyboardHandlers( // of the event. In order to allow the same real event in the future, the // handler is "toggled" when events pass through, therefore the redispatching // algorithm does not allow more than 1 handler that takes |SendInput|. -#ifdef WINUWP - flutter::KeyboardKeyHandler::EventDispatcher redispatch_event = nullptr; - flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = - nullptr; -#else - flutter::KeyboardKeyHandler::EventDispatcher redispatch_event = SendInput; - flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = - GetKeyState; -#endif auto key_handler = - std::make_unique(redispatch_event); + std::make_unique(dispatch_event); key_handler->AddDelegate(std::make_unique( [this](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* user_data) { diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index f70d1b1aa62d6..10241ad5cb083 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -19,6 +19,8 @@ #include "flutter/shell/platform/windows/cursor_handler.h" #include "flutter/shell/platform/windows/flutter_windows_engine.h" #include "flutter/shell/platform/windows/keyboard_handler_base.h" +#include "flutter/shell/platform/windows/keyboard_key_handler.h" +#include "flutter/shell/platform/windows/keyboard_key_embedder_handler.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_delegate.h" @@ -144,7 +146,10 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, protected: // Called to create the keyboard hook handlers. - virtual void RegisterKeyboardHandlers(flutter::BinaryMessenger* messenger); + virtual void RegisterKeyboardHandlers( + flutter::BinaryMessenger* messenger, + flutter::KeyboardKeyHandler::EventDispatcher dispatch_event, + flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state); // Used by RegisterKeyboardHandlers to add a new keyboard hook handler. void AddKeyboardHandler( diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index b6180fc508e65..f8ba822cc9d3d 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -235,7 +235,7 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, // return true at this time, preventing this event from affecting // others. - return true; + return !is_deadchar; } bool KeyboardKeyHandler::RemoveRedispatchedEvent(const PendingEvent& incoming) { diff --git a/shell/platform/windows/keyboard_unittests.cc b/shell/platform/windows/keyboard_unittests.cc new file mode 100644 index 0000000000000..b2b5573d6fc9b --- /dev/null +++ b/shell/platform/windows/keyboard_unittests.cc @@ -0,0 +1,384 @@ +// 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/common/json_message_codec.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" +#include "flutter/shell/platform/embedder/test_utils/key_codes.h" +#include "flutter/shell/platform/windows/flutter_windows_engine.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/keyboard_key_handler.h" +#include "flutter/shell/platform/windows/testing/engine_modifier.h" +#include "flutter/shell/platform/windows/testing/flutter_window_win32_test.h" +#include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h" +#include "flutter/shell/platform/windows/testing/test_keyboard.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include + +using testing::_; +using testing::Invoke; +using testing::Return; +using namespace ::flutter::testing::keycodes; + +namespace flutter { +namespace testing { + +namespace { + +constexpr SHORT kStateMaskToggled = 0x01; +constexpr SHORT kStateMaskPressed = 0x80; + +struct SimulatedEvent { + UINT message; + WPARAM wparam; + LPARAM lparam; +}; + +static LPARAM CreateKeyEventLparam(USHORT scancode, + bool extended, + bool was_down, + 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)); +} + +class MockFlutterWindowWin32 : public FlutterWindowWin32 { + public: + typedef std::function U16StringHandler; + + MockFlutterWindowWin32(U16StringHandler on_text) + : FlutterWindowWin32(800, 600), on_text_(std::move(on_text)) { + ON_CALL(*this, GetDpiScale()) + .WillByDefault(Return(this->FlutterWindowWin32::GetDpiScale())); + } + virtual ~MockFlutterWindowWin32() {} + + // Prevent copying. + MockFlutterWindowWin32(MockFlutterWindowWin32 const&) = delete; + MockFlutterWindowWin32& operator=(MockFlutterWindowWin32 const&) = delete; + + // Wrapper for GetCurrentDPI() which is a protected method. + UINT GetDpi() { return GetCurrentDPI(); } + + // Simulates a WindowProc message from the OS. + LRESULT InjectWindowMessage(UINT const message, + WPARAM const wparam, + LPARAM const lparam) { + return HandleMessage(message, wparam, lparam); + } + + void OnText(const std::u16string& text) override { + on_text_(text); + } + + MOCK_METHOD1(OnDpiScale, void(unsigned int)); + MOCK_METHOD2(OnResize, void(unsigned int, unsigned int)); + MOCK_METHOD2(OnPointerMove, void(double, double)); + MOCK_METHOD3(OnPointerDown, void(double, double, UINT)); + MOCK_METHOD3(OnPointerUp, void(double, double, UINT)); + MOCK_METHOD0(OnPointerLeave, void()); + MOCK_METHOD0(OnSetCursor, void()); + MOCK_METHOD2(OnScroll, void(double, double)); + MOCK_METHOD4(DefaultWindowProc, LRESULT(HWND, UINT, WPARAM, LPARAM)); + MOCK_METHOD0(GetDpiScale, float()); + MOCK_METHOD0(IsVisible, bool()); + MOCK_METHOD1(UpdateCursorRect, void(const Rect&)); + + private: + U16StringHandler on_text_; +}; + +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_; +}; + +// A FlutterWindowsView that overrides the RegisterKeyboardHandlers function +// to register the keyboard hook handlers that can be spied upon. +class TestFlutterWindowsView : public FlutterWindowsView { + public: + TestFlutterWindowsView() + // The WindowBindingHandler is used for window size and such, and doesn't affect keyboard. + : FlutterWindowsView(std::make_unique<::testing::NiceMock>()), + is_printable(true) {} + + void InjectPendingEvents(MockFlutterWindowWin32* win32window) { + while (pending_responds_.size() > 0) { + SimulatedEvent event = pending_responds_.front(); + win32window->InjectWindowMessage(event.message, event.wparam, + event.lparam); + pending_responds_.pop_front(); + } + } + + bool is_printable; + + protected: + void RegisterKeyboardHandlers( + BinaryMessenger* messenger, + KeyboardKeyHandler::EventDispatcher dispatch_event, + KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state) override { + FlutterWindowsView::RegisterKeyboardHandlers(messenger, + [this](UINT cInputs, LPINPUT pInputs, int cbSize) -> UINT { + return this->SendInput(cInputs, pInputs, cbSize); + }, + key_state_.Getter()); + } + + private: + UINT SendInput(UINT cInputs, LPINPUT pInputs, int cbSize) { + // Simulate the event loop by just sending the event sent to + // "SendInput" directly to the window. + const KEYBDINPUT kbdinput = pInputs->ki; + const UINT message = + (kbdinput.dwFlags & KEYEVENTF_KEYUP) ? WM_KEYUP : WM_KEYDOWN; + const bool is_key_up = kbdinput.dwFlags & KEYEVENTF_KEYUP; + const LPARAM lparam = CreateKeyEventLparam( + kbdinput.wScan, kbdinput.dwFlags & KEYEVENTF_EXTENDEDKEY, is_key_up); + pending_responds_.push_back(SimulatedEvent{message, kbdinput.wVk, lparam}); + if (is_printable && (kbdinput.dwFlags & KEYEVENTF_KEYUP) == 0) { + pending_responds_.push_back( + SimulatedEvent{WM_CHAR, kbdinput.wVk, lparam}); + } + return 1; + } + + std::deque pending_responds_; + TestKeystate key_state_; +}; + +// A struct to use as a FlutterPlatformMessageResponseHandle so it can keep the +// callbacks and user data passed to the engine's +// PlatformMessageCreateResponseHandle for use in the SendPlatformMessage +// overridden function. +struct TestResponseHandle { + FlutterDesktopBinaryReply callback; + void* user_data; +}; + +static bool test_response = false; + +typedef enum { + kKeyCallOnKey, + kKeyCallOnText, +} KeyCallType; + +typedef struct { + KeyCallType type; + + // Only one of the following fields should be assigned. + FlutterKeyEvent key_event; + std::u16string text; +} KeyCall; + +static std::vector key_calls; + +void clear_key_calls() { + for (KeyCall& key_call : key_calls) { + if (key_call.type == kKeyCallOnKey && key_call.key_event.character != nullptr) { + delete[] key_call.key_event.character; + } + } + key_calls.clear(); +} + +std::unique_ptr> keyHandlingResponse(bool handled) { + rapidjson::Document document; + auto& allocator = document.GetAllocator(); + document.SetObject(); + document.AddMember("handled", test_response, allocator); + return flutter::JsonMessageCodec::GetInstance().EncodeMessage(document); +} + +// Returns an engine instance configured with dummy project path values, and +// overridden methods for sending platform messages, so that the engine can +// respond as if the framework were connected. +std::unique_ptr GetTestEngine() { + FlutterDesktopEngineProperties properties = {}; + properties.assets_path = L"C:\\foo\\flutter_assets"; + properties.icu_data_path = L"C:\\foo\\icudtl.dat"; + properties.aot_library_path = L"C:\\foo\\aot.so"; + FlutterProjectBundle project(properties); + auto engine = std::make_unique(project); + + EngineModifier modifier(engine.get()); + + // This mock handles channel messages. + modifier.embedder_api().SendPlatformMessage = + [](FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterPlatformMessage* message) { + if (std::string(message->channel) == std::string("flutter/settings")) { + return kSuccess; + } + if (std::string(message->channel) == std::string("flutter/keyevent")) { + auto response = keyHandlingResponse(true); + const TestResponseHandle* response_handle = + reinterpret_cast( + message->response_handle); + if (response_handle->callback != nullptr) { + response_handle->callback(response->data(), response->size(), + response_handle->user_data); + } + return kSuccess; + } + return kSuccess; + }; + + // This mock handles key events sent through the embedder API, + // and records it in `key_calls`. + modifier.embedder_api().SendKeyEvent = + [](FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterKeyEvent* event, + FlutterKeyEventCallback callback, void* user_data) { + FlutterKeyEvent clone_event = *event; + clone_event.character = event->character == nullptr ? nullptr : clone_string(event->character); + key_calls.push_back(KeyCall{ + .type = kKeyCallOnKey, + .key_event = clone_event, + }); + if (callback != nullptr) { + callback(test_response, user_data); + } + return kSuccess; + }; + + // The following mocks enable channel mocking. + modifier.embedder_api().PlatformMessageCreateResponseHandle = + [](auto engine, auto data_callback, auto user_data, auto response_out) { + TestResponseHandle* response_handle = new TestResponseHandle(); + response_handle->user_data = user_data; + response_handle->callback = data_callback; + *response_out = reinterpret_cast( + response_handle); + return kSuccess; + }; + + modifier.embedder_api().PlatformMessageReleaseResponseHandle = + [](FLUTTER_API_SYMBOL(FlutterEngine) engine, + FlutterPlatformMessageResponseHandle* response) { + const TestResponseHandle* response_handle = + reinterpret_cast(response); + delete response_handle; + return kSuccess; + }; + + // The following mocks allows RunWithEntrypoint to be run, which creates a + // non-empty FlutterEngine and enables SendKeyEvent. + + modifier.embedder_api().Run = + [](size_t version, const FlutterRendererConfig* config, + const FlutterProjectArgs* args, void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) { + *engine_out = reinterpret_cast(1); + + return kSuccess; + }; + modifier.embedder_api().UpdateLocales = + [](auto engine, const FlutterLocale** locales, size_t locales_count) { + return kSuccess; + }; + modifier.embedder_api().SendWindowMetricsEvent = + [](auto engine, const FlutterWindowMetricsEvent* event) { + return kSuccess; + }; + modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; }; + + engine->RunWithEntrypoint(nullptr); + return engine; +} + +class KeyboardTester { + public: + explicit KeyboardTester() + { + view_ = std::make_unique(); + view_->SetEngine(std::move(GetTestEngine())); + window_ = std::make_unique( + [](const std::u16string& text) { + key_calls.push_back(KeyCall{ + .type = kKeyCallOnText, + .text = text, + }); + } + ); + window_->SetView(view_.get()); + } + + KeyboardTester& Responding(bool response) { + test_response = response; + return *this; + } + + LRESULT InjectWindowMessage(UINT const message, + WPARAM const wparam, + LPARAM const lparam) { + return window_->InjectWindowMessage(message, wparam, lparam); + } + + private: + std::unique_ptr view_; + std::unique_ptr window_; +}; + +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 kVirtualKeyA = 0x41; + +constexpr bool kExtended = true; +constexpr bool kNotExtended = false; +constexpr bool kWasDown = true; +constexpr bool kWasUp = false; +constexpr bool kSynthesized = true; +constexpr bool kNotSynthesized = false; + +} + +#define EXPECT_CALL_IS_EVENT(_key_call, ...) \ + EXPECT_EQ(_key_call.type, kKeyCallOnKey); \ + EXPECT_EVENT_EQUALS(_key_call.key_event, __VA_ARGS__); + +TEST(KeyboardTest, LowerCaseA) { + KeyboardTester tester; + + tester.Responding(true) + .InjectWindowMessage(WM_KEYDOWN, kVirtualKeyA, CreateKeyEventLparam(kScanCodeKeyA, kNotExtended, kWasUp)); + + EXPECT_EQ(key_calls.size(), 1); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeDown, kPhysicalKeyA, kLogicalKeyA, "A", kNotSynthesized); + clear_key_calls(); + + tester.Responding(true) + .InjectWindowMessage(WM_KEYUP, kVirtualKeyA, CreateKeyEventLparam(kScanCodeKeyA, kNotExtended, kWasDown)); + + EXPECT_EQ(key_calls.size(), 1); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized); + clear_key_calls(); +} + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/windows/testing/test_keyboard.cc b/shell/platform/windows/testing/test_keyboard.cc new file mode 100644 index 0000000000000..a28aa72067c7f --- /dev/null +++ b/shell/platform/windows/testing/test_keyboard.cc @@ -0,0 +1,21 @@ +// 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/testing/test_keyboard.h" + +namespace flutter { +namespace testing { + +char* clone_string(const char* string) { + if (string == nullptr) { + return nullptr; + } + size_t len = strlen(string); + char* result = new char[len + 1]; + strcpy(result, string); + return result; +} + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/windows/testing/test_keyboard.h b/shell/platform/windows/testing/test_keyboard.h new file mode 100644 index 0000000000000..666e461ec670b --- /dev/null +++ b/shell/platform/windows/testing/test_keyboard.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_SHELL_PLATFORM_WINDOWS_TESTING_TEST_KEYBOARD_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_TEST_KEYBOARD_H_ + +#include + +#include "flutter/shell/platform/embedder/embedder.h" + +#include "gtest/gtest.h" + +#define _RETURN_IF_NOT_EQUALS(val1, val2) \ + if ((val1) != (val2)) { \ + return ::testing::AssertionFailure() \ + << "Expected equality of these values:\n "#val1"\n To be: " << val2 << "\n Actual: \n " << val1; \ + } + +namespace flutter { +namespace testing { + +namespace { + +std::string _print_character(const char* s) { + if (s == nullptr) { + return "nullptr"; + } + return std::string("\"") + s + "\""; +} + +::testing::AssertionResult _EventEquals(const char* expr_event, const char* expr_expected, const FlutterKeyEvent& event, const FlutterKeyEvent& expected) { + _RETURN_IF_NOT_EQUALS(event.struct_size, sizeof(FlutterKeyEvent)); + _RETURN_IF_NOT_EQUALS(event.type, expected.type); + _RETURN_IF_NOT_EQUALS(event.physical, expected.physical); + _RETURN_IF_NOT_EQUALS(event.logical, expected.logical); + if ((event.character == nullptr) != (expected.character == nullptr) || strcmp(event.character, expected.character) != 0) { + return ::testing::AssertionFailure() + << "Expected equality of these values:\n expected.character\n " << _print_character(expected.character) << "\n Actual: \n " << _print_character(event.character); + } + _RETURN_IF_NOT_EQUALS(event.synthesized, expected.synthesized); + return ::testing::AssertionSuccess(); +} + +} // namespace + +// Clone string onto the heap. +// +// If #string is nullptr, returns nullptr. Otherwise, the returned pointer must +// be freed with delete[]. +char* clone_string(const char* string); + +} // namespace testing +} // namespace flutter + +#define EXPECT_EVENT_EQUALS(_target, _type, _physical, _logical, _character, _synthesized) \ + EXPECT_PRED_FORMAT2(_EventEquals, _target, (FlutterKeyEvent{\ + .type = _type, \ + .physical = _physical, \ + .logical = _logical, \ + .character = _character, \ + .synthesized = _synthesized, \ + })); + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_TEST_KEYBOARD_H_ diff --git a/shell/platform/windows/window_win32.cc b/shell/platform/windows/window_win32.cc index 06fd980a19378..84bf8f1de5a64 100644 --- a/shell/platform/windows/window_win32.cc +++ b/shell/platform/windows/window_win32.cc @@ -375,7 +375,7 @@ WindowWin32::HandleMessage(UINT const message, // - Lead surrogates, which like dead keys will be send once combined. // - ASCII control characters, which are sent as WM_CHAR events for all // control key shortcuts. - if (!handled_for_char_message_ && message == WM_CHAR && s_pending_high_surrogate == 0 && + if (message == WM_CHAR && s_pending_high_surrogate == 0 && character >= u' ') { OnText(text); } From 51b5ada8dc9178ac960f81fea87554a13d356de3 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 5 Aug 2021 16:13:44 -0700 Subject: [PATCH 7/7] Format --- shell/platform/windows/BUILD.gn | 2 +- .../windows/flutter_window_win32_unittests.cc | 3 +- .../platform/windows/flutter_windows_view.cc | 3 +- shell/platform/windows/flutter_windows_view.h | 2 +- .../platform/windows/keyboard_key_handler.cc | 4 +- shell/platform/windows/keyboard_unittests.cc | 70 ++++++++++--------- .../platform/windows/testing/test_keyboard.h | 41 ++++++----- shell/platform/windows/window_win32.cc | 10 ++- 8 files changed, 78 insertions(+), 57 deletions(-) diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 8936d1cd32caa..3a9f5fa0c3c20 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -240,10 +240,10 @@ executable("flutter_windows_unittests") { "flutter_windows_engine_unittests.cc", "flutter_windows_texture_registrar_unittests.cc", "flutter_windows_view_unittests.cc", - "keyboard_unittests.cc", "keyboard_key_channel_handler_unittests.cc", "keyboard_key_embedder_handler_unittests.cc", "keyboard_key_handler_unittests.cc", + "keyboard_unittests.cc", "testing/flutter_window_win32_test.cc", "testing/flutter_window_win32_test.h", "testing/mock_window_binding_handler.cc", diff --git a/shell/platform/windows/flutter_window_win32_unittests.cc b/shell/platform/windows/flutter_window_win32_unittests.cc index 1435afb13b409..0fca408911421 100644 --- a/shell/platform/windows/flutter_window_win32_unittests.cc +++ b/shell/platform/windows/flutter_window_win32_unittests.cc @@ -218,7 +218,8 @@ class TestFlutterWindowsView : public FlutterWindowsView { void RegisterKeyboardHandlers( flutter::BinaryMessenger* messenger, flutter::KeyboardKeyHandler::EventDispatcher dispatch_event, - flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state) override { + flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state) + override { auto spy_key_event_handler = std::make_unique( messenger, [this](UINT cInputs, LPINPUT pInputs, int cbSize) -> UINT { return this->SendInput(cInputs, pInputs, cbSize); diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 356a590e967f0..fee32e5f7da18 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -64,7 +64,8 @@ void FlutterWindowsView::SetEngine( flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = GetKeyState; #endif - RegisterKeyboardHandlers(internal_plugin_messenger, dispatch_event, get_key_state); + RegisterKeyboardHandlers(internal_plugin_messenger, dispatch_event, + get_key_state); platform_handler_ = PlatformHandler::Create(internal_plugin_messenger, this); cursor_handler_ = std::make_unique( internal_plugin_messenger, binding_handler_.get()); diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 10241ad5cb083..f3432c0575062 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -19,8 +19,8 @@ #include "flutter/shell/platform/windows/cursor_handler.h" #include "flutter/shell/platform/windows/flutter_windows_engine.h" #include "flutter/shell/platform/windows/keyboard_handler_base.h" -#include "flutter/shell/platform/windows/keyboard_key_handler.h" #include "flutter/shell/platform/windows/keyboard_key_embedder_handler.h" +#include "flutter/shell/platform/windows/keyboard_key_handler.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_delegate.h" diff --git a/shell/platform/windows/keyboard_key_handler.cc b/shell/platform/windows/keyboard_key_handler.cc index f8ba822cc9d3d..34c3515ba6ae3 100644 --- a/shell/platform/windows/keyboard_key_handler.cc +++ b/shell/platform/windows/keyboard_key_handler.cc @@ -223,8 +223,8 @@ bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view, const bool is_deadchar = character & 0x80000000; const uint32_t key_character = is_deadchar ? 0 : character; for (const auto& delegate : delegates_) { - delegate->KeyboardHook(key, scancode, action, key_character, extended, was_down, - [sequence_id, this](bool handled) { + delegate->KeyboardHook(key, scancode, action, key_character, extended, + was_down, [sequence_id, this](bool handled) { ResolvePendingEvent(sequence_id, handled); }); } diff --git a/shell/platform/windows/keyboard_unittests.cc b/shell/platform/windows/keyboard_unittests.cc index b2b5573d6fc9b..7a98fb6ee00d4 100644 --- a/shell/platform/windows/keyboard_unittests.cc +++ b/shell/platform/windows/keyboard_unittests.cc @@ -4,8 +4,8 @@ #include "flutter/shell/platform/common/json_message_codec.h" #include "flutter/shell/platform/embedder/embedder.h" -#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" #include "flutter/shell/platform/embedder/test_utils/key_codes.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_channel_handler.h" #include "flutter/shell/platform/windows/keyboard_key_embedder_handler.h" @@ -56,7 +56,7 @@ class MockFlutterWindowWin32 : public FlutterWindowWin32 { typedef std::function U16StringHandler; MockFlutterWindowWin32(U16StringHandler on_text) - : FlutterWindowWin32(800, 600), on_text_(std::move(on_text)) { + : FlutterWindowWin32(800, 600), on_text_(std::move(on_text)) { ON_CALL(*this, GetDpiScale()) .WillByDefault(Return(this->FlutterWindowWin32::GetDpiScale())); } @@ -76,9 +76,7 @@ class MockFlutterWindowWin32 : public FlutterWindowWin32 { return HandleMessage(message, wparam, lparam); } - void OnText(const std::u16string& text) override { - on_text_(text); - } + void OnText(const std::u16string& text) override { on_text_(text); } MOCK_METHOD1(OnDpiScale, void(unsigned int)); MOCK_METHOD2(OnResize, void(unsigned int, unsigned int)); @@ -119,8 +117,10 @@ class TestKeystate { class TestFlutterWindowsView : public FlutterWindowsView { public: TestFlutterWindowsView() - // The WindowBindingHandler is used for window size and such, and doesn't affect keyboard. - : FlutterWindowsView(std::make_unique<::testing::NiceMock>()), + // The WindowBindingHandler is used for window size and such, and doesn't + // affect keyboard. + : FlutterWindowsView( + std::make_unique<::testing::NiceMock>()), is_printable(true) {} void InjectPendingEvents(MockFlutterWindowWin32* win32window) { @@ -139,11 +139,12 @@ class TestFlutterWindowsView : public FlutterWindowsView { BinaryMessenger* messenger, KeyboardKeyHandler::EventDispatcher dispatch_event, KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state) override { - FlutterWindowsView::RegisterKeyboardHandlers(messenger, - [this](UINT cInputs, LPINPUT pInputs, int cbSize) -> UINT { - return this->SendInput(cInputs, pInputs, cbSize); - }, - key_state_.Getter()); + FlutterWindowsView::RegisterKeyboardHandlers( + messenger, + [this](UINT cInputs, LPINPUT pInputs, int cbSize) -> UINT { + return this->SendInput(cInputs, pInputs, cbSize); + }, + key_state_.Getter()); } private: @@ -196,7 +197,8 @@ static std::vector key_calls; void clear_key_calls() { for (KeyCall& key_call : key_calls) { - if (key_call.type == kKeyCallOnKey && key_call.key_event.character != nullptr) { + if (key_call.type == kKeyCallOnKey && + key_call.key_event.character != nullptr) { delete[] key_call.key_event.character; } } @@ -251,10 +253,12 @@ std::unique_ptr GetTestEngine() { [](FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterKeyEvent* event, FlutterKeyEventCallback callback, void* user_data) { FlutterKeyEvent clone_event = *event; - clone_event.character = event->character == nullptr ? nullptr : clone_string(event->character); + clone_event.character = event->character == nullptr + ? nullptr + : clone_string(event->character); key_calls.push_back(KeyCall{ - .type = kKeyCallOnKey, - .key_event = clone_event, + .type = kKeyCallOnKey, + .key_event = clone_event, }); if (callback != nullptr) { callback(test_response, user_data); @@ -309,18 +313,16 @@ std::unique_ptr GetTestEngine() { class KeyboardTester { public: - explicit KeyboardTester() - { + explicit KeyboardTester() { view_ = std::make_unique(); view_->SetEngine(std::move(GetTestEngine())); window_ = std::make_unique( - [](const std::u16string& text) { - key_calls.push_back(KeyCall{ - .type = kKeyCallOnText, - .text = text, + [](const std::u16string& text) { + key_calls.push_back(KeyCall{ + .type = kKeyCallOnText, + .text = text, + }); }); - } - ); window_->SetView(view_.get()); } @@ -356,27 +358,31 @@ constexpr bool kWasUp = false; constexpr bool kSynthesized = true; constexpr bool kNotSynthesized = false; -} +} // namespace #define EXPECT_CALL_IS_EVENT(_key_call, ...) \ - EXPECT_EQ(_key_call.type, kKeyCallOnKey); \ + EXPECT_EQ(_key_call.type, kKeyCallOnKey); \ EXPECT_EVENT_EQUALS(_key_call.key_event, __VA_ARGS__); TEST(KeyboardTest, LowerCaseA) { KeyboardTester tester; - tester.Responding(true) - .InjectWindowMessage(WM_KEYDOWN, kVirtualKeyA, CreateKeyEventLparam(kScanCodeKeyA, kNotExtended, kWasUp)); + tester.Responding(true).InjectWindowMessage( + WM_KEYDOWN, kVirtualKeyA, + CreateKeyEventLparam(kScanCodeKeyA, kNotExtended, kWasUp)); EXPECT_EQ(key_calls.size(), 1); - EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeDown, kPhysicalKeyA, kLogicalKeyA, "A", kNotSynthesized); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeDown, kPhysicalKeyA, + kLogicalKeyA, "A", kNotSynthesized); clear_key_calls(); - tester.Responding(true) - .InjectWindowMessage(WM_KEYUP, kVirtualKeyA, CreateKeyEventLparam(kScanCodeKeyA, kNotExtended, kWasDown)); + tester.Responding(true).InjectWindowMessage( + WM_KEYUP, kVirtualKeyA, + CreateKeyEventLparam(kScanCodeKeyA, kNotExtended, kWasDown)); EXPECT_EQ(key_calls.size(), 1); - EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalKeyA, + kLogicalKeyA, "", kNotSynthesized); clear_key_calls(); } diff --git a/shell/platform/windows/testing/test_keyboard.h b/shell/platform/windows/testing/test_keyboard.h index 666e461ec670b..a7728ce015b03 100644 --- a/shell/platform/windows/testing/test_keyboard.h +++ b/shell/platform/windows/testing/test_keyboard.h @@ -11,10 +11,11 @@ #include "gtest/gtest.h" -#define _RETURN_IF_NOT_EQUALS(val1, val2) \ - if ((val1) != (val2)) { \ - return ::testing::AssertionFailure() \ - << "Expected equality of these values:\n "#val1"\n To be: " << val2 << "\n Actual: \n " << val1; \ +#define _RETURN_IF_NOT_EQUALS(val1, val2) \ + if ((val1) != (val2)) { \ + return ::testing::AssertionFailure() \ + << "Expected equality of these values:\n " #val1 "\n To be: " \ + << val2 << "\n Actual: \n " << val1; \ } namespace flutter { @@ -29,14 +30,20 @@ std::string _print_character(const char* s) { return std::string("\"") + s + "\""; } -::testing::AssertionResult _EventEquals(const char* expr_event, const char* expr_expected, const FlutterKeyEvent& event, const FlutterKeyEvent& expected) { +::testing::AssertionResult _EventEquals(const char* expr_event, + const char* expr_expected, + const FlutterKeyEvent& event, + const FlutterKeyEvent& expected) { _RETURN_IF_NOT_EQUALS(event.struct_size, sizeof(FlutterKeyEvent)); _RETURN_IF_NOT_EQUALS(event.type, expected.type); _RETURN_IF_NOT_EQUALS(event.physical, expected.physical); _RETURN_IF_NOT_EQUALS(event.logical, expected.logical); - if ((event.character == nullptr) != (expected.character == nullptr) || strcmp(event.character, expected.character) != 0) { + if ((event.character == nullptr) != (expected.character == nullptr) || + strcmp(event.character, expected.character) != 0) { return ::testing::AssertionFailure() - << "Expected equality of these values:\n expected.character\n " << _print_character(expected.character) << "\n Actual: \n " << _print_character(event.character); + << "Expected equality of these values:\n expected.character\n " + << _print_character(expected.character) << "\n Actual: \n " + << _print_character(event.character); } _RETURN_IF_NOT_EQUALS(event.synthesized, expected.synthesized); return ::testing::AssertionSuccess(); @@ -53,13 +60,15 @@ char* clone_string(const char* string); } // namespace testing } // namespace flutter -#define EXPECT_EVENT_EQUALS(_target, _type, _physical, _logical, _character, _synthesized) \ - EXPECT_PRED_FORMAT2(_EventEquals, _target, (FlutterKeyEvent{\ - .type = _type, \ - .physical = _physical, \ - .logical = _logical, \ - .character = _character, \ - .synthesized = _synthesized, \ - })); +#define EXPECT_EVENT_EQUALS(_target, _type, _physical, _logical, _character, \ + _synthesized) \ + EXPECT_PRED_FORMAT2(_EventEquals, _target, \ + (FlutterKeyEvent{ \ + .type = _type, \ + .physical = _physical, \ + .logical = _logical, \ + .character = _character, \ + .synthesized = _synthesized, \ + })); -#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_TEST_KEYBOARD_H_ +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_TEST_KEYBOARD_H_ diff --git a/shell/platform/windows/window_win32.cc b/shell/platform/windows/window_win32.cc index 84bf8f1de5a64..1ed1333f45f16 100644 --- a/shell/platform/windows/window_win32.cc +++ b/shell/platform/windows/window_win32.cc @@ -172,11 +172,14 @@ void WindowWin32::UpdateCursorRect(const Rect& rect) { text_input_manager_.UpdateCaretRect(rect); } -static uint16_t ResolveKeyCode(uint16_t original, bool extended, uint8_t scancode) { +static uint16_t ResolveKeyCode(uint16_t original, + bool extended, + uint8_t scancode) { switch (original) { case VK_SHIFT: case VK_LSHIFT: - return MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX);; + return MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX); + ; case VK_MENU: case VK_LMENU: return extended ? VK_RMENU : VK_LMENU; @@ -351,7 +354,8 @@ WindowWin32::HandleMessage(UINT const message, // Key presses that generate a non-surrogate should be sent from // 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 && (IS_HIGH_SURROGATE(character) || IS_LOW_SURROGATE(character))) { + if (keycode_for_char_message_ != 0 && + (IS_HIGH_SURROGATE(character) || IS_LOW_SURROGATE(character))) { const unsigned int scancode = (lparam >> 16) & 0xff; const bool extended = ((lparam >> 24) & 0x01) == 0x01; const bool was_down = lparam & 0x40000000;