From 53f9995b723b3b206c113ebde43011652d875d86 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 26 Jan 2022 03:59:06 -0800 Subject: [PATCH 01/12] Impl --- .../windows/keyboard_manager_win32.cc | 48 ++++++++++--------- .../platform/windows/keyboard_manager_win32.h | 19 +++++--- .../windows/keyboard_win32_unittests.cc | 10 ++++ shell/platform/windows/window_win32.cc | 4 ++ shell/platform/windows/window_win32.h | 3 ++ 5 files changed, 56 insertions(+), 28 deletions(-) diff --git a/shell/platform/windows/keyboard_manager_win32.cc b/shell/platform/windows/keyboard_manager_win32.cc index ba9014ca78728..48668aed5e78a 100644 --- a/shell/platform/windows/keyboard_manager_win32.cc +++ b/shell/platform/windows/keyboard_manager_win32.cc @@ -153,7 +153,7 @@ void KeyboardManagerWin32::DispatchEvent(const PendingEvent& event) { }, }; - UINT accepted = window_delegate_->Win32DispatchEvent(1, &input_event, + UINT accepted = window_delegate_->Win32DispatchEvent(1, &input_event, sizeof(input_event)); if (accepted != 1) { std::cerr << "Unable to synthesize event for keyboard event with scancode " @@ -167,21 +167,33 @@ void KeyboardManagerWin32::DispatchEvent(const PendingEvent& event) { void KeyboardManagerWin32::RedispatchEvent( std::unique_ptr event) { - DispatchEvent(*event); + for (const Win32Message& message : event->session) { + UINT accepted = 1; + if (message.action != WM_CHAR) { + accepted = window_delegate_->Win32DispatchMessage( + message.action, message.wparam, message.lparam); + } + pending_redispatches_.push_back(message); + if (accepted != 1) { + std::cerr << "Unable to synthesize event for keyboard event. (" + << accepted << ")" + << std::endl; + } + } if (pending_redispatches_.size() > kMaxPendingEvents) { std::cerr << "There are " << pending_redispatches_.size() << " keyboard events that have not yet received a response from the " << "framework. Are responses being sent?" << std::endl; } - pending_redispatches_.push_back(std::move(event)); } -bool KeyboardManagerWin32::RemoveRedispatchedEvent( - const PendingEvent& incoming) { +bool KeyboardManagerWin32::RemoveRedispatchedMessage(UINT const action, + WPARAM const wparam, + LPARAM const lparam) { for (auto iter = pending_redispatches_.begin(); iter != pending_redispatches_.end(); ++iter) { - if ((*iter)->Hash() == incoming.Hash()) { + if (action == iter->action && lparam == iter->lparam) { pending_redispatches_.erase(iter); return true; } @@ -189,12 +201,8 @@ bool KeyboardManagerWin32::RemoveRedispatchedEvent( return false; } -bool KeyboardManagerWin32::OnKey(std::unique_ptr event, +void KeyboardManagerWin32::OnKey(std::unique_ptr event, OnKeyCallback callback) { - if (RemoveRedispatchedEvent(*event)) { - return false; - } - if (IsKeyDownAltRight(event->action, event->key, event->extended)) { if (last_key_is_ctrl_left_down) { should_synthesize_ctrl_left_up = true; @@ -228,7 +236,6 @@ bool KeyboardManagerWin32::OnKey(std::unique_ptr event, callback(std::unique_ptr(event), handled); }); - return true; } void KeyboardManagerWin32::HandleOnKeyResult( @@ -273,6 +280,9 @@ void KeyboardManagerWin32::HandleOnKeyResult( bool KeyboardManagerWin32::HandleMessage(UINT const action, WPARAM const wparam, LPARAM const lparam) { + if (RemoveRedispatchedMessage(action, wparam, lparam)) { + return false; + } switch (action) { case WM_DEADCHAR: case WM_SYSDEADCHAR: @@ -342,16 +352,13 @@ bool KeyboardManagerWin32::HandleMessage(UINT const action, .was_down = was_down, .session = std::move(current_session_), }); - const bool is_unmet_event = OnKey( + OnKey( std::move(event), [this, char_action = action, text]( std::unique_ptr event, bool handled) { HandleOnKeyResult(std::move(event), handled, char_action, text); }); - const bool is_syskey = action == WM_SYSCHAR; - // For system characters, always pass them to the default WndProc so - // that system keys like the ALT-TAB are processed correctly. - return is_unmet_event && !is_syskey; + return true; } // If the charcter session is not preceded by a key down message, dispatch @@ -415,15 +422,12 @@ bool KeyboardManagerWin32::HandleMessage(UINT const action, .was_down = was_down, .session = std::move(current_session_), }); - const bool is_unmet_event = OnKey( + OnKey( std::move(event), [this](std::unique_ptr event, bool handled) { HandleOnKeyResult(std::move(event), handled, 0, std::u16string()); }); - const bool is_syskey = action == WM_SYSKEYDOWN || action == WM_SYSKEYUP; - // For system keys, always pass them to the default WndProc so that keys - // like the ALT-TAB or Kanji switches are processed correctly. - return is_unmet_event && !is_syskey; + return true; } default: assert(false); diff --git a/shell/platform/windows/keyboard_manager_win32.h b/shell/platform/windows/keyboard_manager_win32.h index 8cd1dd49a81d1..c5f99b1d38585 100644 --- a/shell/platform/windows/keyboard_manager_win32.h +++ b/shell/platform/windows/keyboard_manager_win32.h @@ -81,6 +81,13 @@ class KeyboardManagerWin32 { virtual UINT Win32DispatchEvent(UINT cInputs, LPINPUT pInputs, int cbSize) = 0; + + // Win32's |SendMessage|. + // + // Used to synthesize key messages. + virtual UINT Win32DispatchMessage(UINT Msg, + WPARAM wParam, + LPARAM lParam) = 0; }; using KeyEventCallback = WindowDelegate::KeyEventCallback; @@ -105,9 +112,9 @@ class KeyboardManagerWin32 { private: struct Win32Message { - UINT const action; - WPARAM const wparam; - LPARAM const lparam; + UINT action; + WPARAM wparam; + LPARAM lparam; bool IsHighSurrogate() const { return IS_HIGH_SURROGATE(wparam); } @@ -137,7 +144,7 @@ class KeyboardManagerWin32 { std::function, bool)>; // Returns true if it's a new event, or false if it's a redispatched event. - bool OnKey(std::unique_ptr event, OnKeyCallback callback); + void OnKey(std::unique_ptr event, OnKeyCallback callback); void HandleOnKeyResult(std::unique_ptr event, bool handled, @@ -158,7 +165,7 @@ class KeyboardManagerWin32 { // // 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); + bool RemoveRedispatchedMessage(UINT action, WPARAM wparam, LPARAM lparam); void RedispatchEvent(std::unique_ptr event); WindowDelegate* window_delegate_; @@ -185,7 +192,7 @@ class KeyboardManagerWin32 { // The queue of key events that have been redispatched to the system but have // not yet been received for a second time. - std::deque> pending_redispatches_; + std::deque pending_redispatches_; // Calculate a hash based on event data for fast comparison for a redispatched // event. diff --git a/shell/platform/windows/keyboard_win32_unittests.cc b/shell/platform/windows/keyboard_win32_unittests.cc index e3aecbdef3974..b8b66346d1862 100644 --- a/shell/platform/windows/keyboard_win32_unittests.cc +++ b/shell/platform/windows/keyboard_win32_unittests.cc @@ -148,11 +148,21 @@ class MockKeyboardManagerWin32Delegate return 1; } + UINT Win32DispatchMessage(UINT Msg, WPARAM wParam, LPARAM lParam) override { + pending_messages_.push_back(Win32Message{ + .message = Msg, + .wParam = wParam, + .lParam = lParam, + }); + return 1; + } + private: WindowBindingHandlerDelegate* view_; std::unique_ptr keyboard_manager_; MapVkToCharHandler map_vk_to_char_; std::vector pending_responds_; + std::vector pending_messages_; }; class TestKeystate { diff --git a/shell/platform/windows/window_win32.cc b/shell/platform/windows/window_win32.cc index 965ef01bef73a..f2df5ebe920b7 100644 --- a/shell/platform/windows/window_win32.cc +++ b/shell/platform/windows/window_win32.cc @@ -557,4 +557,8 @@ UINT WindowWin32::Win32DispatchEvent(UINT cInputs, return ::SendInput(cInputs, pInputs, cbSize); } +UINT WindowWin32::Win32DispatchMessage(UINT Msg, WPARAM wParam, LPARAM lParam) { + return ::PostMessage(window_handle_, Msg, wParam, lParam); +} + } // namespace flutter diff --git a/shell/platform/windows/window_win32.h b/shell/platform/windows/window_win32.h index e5d292fcb7556..c32883c00943b 100644 --- a/shell/platform/windows/window_win32.h +++ b/shell/platform/windows/window_win32.h @@ -53,6 +53,9 @@ class WindowWin32 : public KeyboardManagerWin32::WindowDelegate { LPINPUT pInputs, int cbSize) override; + // |KeyboardManagerWin32::WindowDelegate| + virtual UINT Win32DispatchMessage(UINT Msg, WPARAM wParam, LPARAM lParam) override; + protected: // Converts a c string to a wide unicode string. std::wstring NarrowToWide(const char* source); From be5c93639a6e622a3280ac3cb22ca195235151fa Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 1 Feb 2022 01:35:10 -0800 Subject: [PATCH 02/12] SendMessage --- shell/platform/windows/keyboard_manager_win32.cc | 11 +++++------ shell/platform/windows/window_win32.cc | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/shell/platform/windows/keyboard_manager_win32.cc b/shell/platform/windows/keyboard_manager_win32.cc index 48668aed5e78a..58275bea7138f 100644 --- a/shell/platform/windows/keyboard_manager_win32.cc +++ b/shell/platform/windows/keyboard_manager_win32.cc @@ -168,15 +168,14 @@ void KeyboardManagerWin32::DispatchEvent(const PendingEvent& event) { void KeyboardManagerWin32::RedispatchEvent( std::unique_ptr event) { for (const Win32Message& message : event->session) { - UINT accepted = 1; + UINT result = 0; if (message.action != WM_CHAR) { - accepted = window_delegate_->Win32DispatchMessage( + pending_redispatches_.push_back(message); + result = window_delegate_->Win32DispatchMessage( message.action, message.wparam, message.lparam); } - pending_redispatches_.push_back(message); - if (accepted != 1) { - std::cerr << "Unable to synthesize event for keyboard event. (" - << accepted << ")" + if (result != 0) { + std::cerr << "Unable to synthesize event for keyboard event." << std::endl; } } diff --git a/shell/platform/windows/window_win32.cc b/shell/platform/windows/window_win32.cc index f2df5ebe920b7..16fd60116f431 100644 --- a/shell/platform/windows/window_win32.cc +++ b/shell/platform/windows/window_win32.cc @@ -558,7 +558,7 @@ UINT WindowWin32::Win32DispatchEvent(UINT cInputs, } UINT WindowWin32::Win32DispatchMessage(UINT Msg, WPARAM wParam, LPARAM lParam) { - return ::PostMessage(window_handle_, Msg, wParam, lParam); + return ::SendMessage(window_handle_, Msg, wParam, lParam); } } // namespace flutter From 4524f17d6da6b88f7ad26ba66a493cd982c55bf5 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 1 Feb 2022 01:43:17 -0800 Subject: [PATCH 03/12] Also redispatch char --- shell/platform/windows/keyboard_manager_win32.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/shell/platform/windows/keyboard_manager_win32.cc b/shell/platform/windows/keyboard_manager_win32.cc index 58275bea7138f..054423cd0f08b 100644 --- a/shell/platform/windows/keyboard_manager_win32.cc +++ b/shell/platform/windows/keyboard_manager_win32.cc @@ -169,11 +169,9 @@ void KeyboardManagerWin32::RedispatchEvent( std::unique_ptr event) { for (const Win32Message& message : event->session) { UINT result = 0; - if (message.action != WM_CHAR) { - pending_redispatches_.push_back(message); - result = window_delegate_->Win32DispatchMessage( - message.action, message.wparam, message.lparam); - } + pending_redispatches_.push_back(message); + result = window_delegate_->Win32DispatchMessage( + message.action, message.wparam, message.lparam); if (result != 0) { std::cerr << "Unable to synthesize event for keyboard event." << std::endl; @@ -192,7 +190,7 @@ bool KeyboardManagerWin32::RemoveRedispatchedMessage(UINT const action, LPARAM const lparam) { for (auto iter = pending_redispatches_.begin(); iter != pending_redispatches_.end(); ++iter) { - if (action == iter->action && lparam == iter->lparam) { + if (action == iter->action && wparam == iter->wparam) { pending_redispatches_.erase(iter); return true; } From 5f88e080bb010a567a80f4388994945d6e8d8c20 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 1 Feb 2022 21:36:53 -0800 Subject: [PATCH 04/12] Remove Win32DispatchEvent. 2 tests failing. --- .../windows/flutter_window_win32_unittests.cc | 85 +--- .../windows/keyboard_manager_win32.cc | 60 +-- .../platform/windows/keyboard_manager_win32.h | 22 +- .../windows/keyboard_win32_unittests.cc | 366 ++---------------- shell/platform/windows/window_win32.cc | 6 - shell/platform/windows/window_win32.h | 5 - 6 files changed, 66 insertions(+), 478 deletions(-) diff --git a/shell/platform/windows/flutter_window_win32_unittests.cc b/shell/platform/windows/flutter_window_win32_unittests.cc index b93d5b85807a9..250071c862291 100644 --- a/shell/platform/windows/flutter_window_win32_unittests.cc +++ b/shell/platform/windows/flutter_window_win32_unittests.cc @@ -93,13 +93,9 @@ class SpyTextInputPlugin : public TextInputPlugin, std::unique_ptr real_implementation_; }; -class MockFlutterWindowWin32 : public FlutterWindowWin32, - public MockMessageQueue { +class MockFlutterWindowWin32 : public FlutterWindowWin32 { public: - MockFlutterWindowWin32(WPARAM virtual_key = 0, bool is_printable = true) - : virtual_key_(virtual_key), - is_printable_(is_printable), - FlutterWindowWin32(800, 600) { + MockFlutterWindowWin32() : FlutterWindowWin32(800, 600) { ON_CALL(*this, GetDpiScale()) .WillByDefault(Return(this->FlutterWindowWin32::GetDpiScale())); } @@ -112,25 +108,6 @@ class MockFlutterWindowWin32 : public FlutterWindowWin32, // 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 Win32SendMessage(message, wparam, lparam); - } - - void InjectMessages(int count, Win32Message message1, ...) { - Win32Message messages[count]; - messages[0] = message1; - va_list args; - va_start(args, message1); - for (int i = 1; i < count; i += 1) { - messages[i] = va_arg(args, Win32Message); - } - va_end(args); - InjectMessageList(count, messages); - } - MOCK_METHOD1(OnDpiScale, void(unsigned int)); MOCK_METHOD2(OnResize, void(unsigned int, unsigned int)); MOCK_METHOD4(OnPointerMove, @@ -147,17 +124,11 @@ class MockFlutterWindowWin32 : public FlutterWindowWin32, MOCK_METHOD0(IsVisible, bool()); MOCK_METHOD1(UpdateCursorRect, void(const Rect&)); MOCK_METHOD0(OnResetImeComposing, void()); + MOCK_METHOD3(Win32DispatchMessage, UINT(UINT, WPARAM, LPARAM)); + MOCK_METHOD4(Win32PeekMessage, BOOL(LPMSG, UINT, UINT, UINT)); + MOCK_METHOD1(Win32MapVkToChar, uint32_t(uint32_t )); protected: - // |KeyboardManagerWin32::WindowDelegate| - BOOL Win32PeekMessage(LPMSG lpMsg, - UINT wMsgFilterMin, - UINT wMsgFilterMax, - UINT wRemoveMsg) override { - return MockMessageQueue::Win32PeekMessage(lpMsg, wMsgFilterMin, - wMsgFilterMax, wRemoveMsg); - } - // |KeyboardManagerWin32::WindowDelegate| LRESULT Win32DefWindowProc(HWND hWnd, UINT Msg, @@ -165,52 +136,6 @@ class MockFlutterWindowWin32 : public FlutterWindowWin32, LPARAM lParam) override { return kWmResultDefault; } - - // |KeyboardManagerWin32::WindowDelegate| - UINT Win32DispatchEvent(UINT cInputs, LPINPUT pInputs, int cbSize) override { - for (UINT input_idx = 0; input_idx < cInputs; input_idx += 1) { - SendInput(pInputs[input_idx].ki); - } - return 1; - } - - // |MockMessageQueue| - LRESULT Win32SendMessage(UINT const message, - WPARAM const wparam, - LPARAM const lparam) override { - return HandleMessage(message, wparam, lparam); - } - - private: - UINT SendInput(KEYBDINPUT kbdinput) { - // Simulate the event loop by just sending the event sent to - // "SendInput" directly to the window. - const bool is_key_up = kbdinput.dwFlags & KEYEVENTF_KEYUP; - const UINT message = is_key_up ? WM_KEYUP : WM_KEYDOWN; - - const LPARAM lparam = CreateKeyEventLparam( - 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 - // Windows will fill it in). - // - // TODO(dkwingsmt): Don't check the message results for redispatched - // messages for now, because making them work takes non-trivial rework - // to our current structure. https://github.com/flutter/flutter/issues/87843 - // If this is resolved, change them to kWmResultDefault. - pending_responds_.push_back( - Win32Message{message, virtual_key_, lparam, kWmResultDontCheck}); - if (is_printable_ && (kbdinput.dwFlags & KEYEVENTF_KEYUP) == 0) { - pending_responds_.push_back( - Win32Message{WM_CHAR, virtual_key_, lparam, kWmResultDontCheck}); - } - return 1; - } - - std::vector pending_responds_; - WPARAM virtual_key_; - bool is_printable_; }; class MockWindowBindingHandlerDelegate : public WindowBindingHandlerDelegate { diff --git a/shell/platform/windows/keyboard_manager_win32.cc b/shell/platform/windows/keyboard_manager_win32.cc index 054423cd0f08b..6334715b030ed 100644 --- a/shell/platform/windows/keyboard_manager_win32.cc +++ b/shell/platform/windows/keyboard_manager_win32.cc @@ -133,38 +133,6 @@ KeyboardManagerWin32::KeyboardManagerWin32(WindowDelegate* delegate) last_key_is_ctrl_left_down(false), should_synthesize_ctrl_left_up(false) {} -void KeyboardManagerWin32::DispatchEvent(const PendingEvent& event) { - assert(event.action != WM_SYSKEYDOWN && event.action != WM_SYSKEYUP && - "Unexpectedly dispatching a SYS event. SYS events can't be dispatched " - "and should have been prevented in earlier code."); - - char32_t character = event.character; - - INPUT input_event{ - .type = INPUT_KEYBOARD, - .ki = - KEYBDINPUT{ - .wVk = static_cast(event.key), - .wScan = static_cast(event.scancode), - .dwFlags = static_cast( - KEYEVENTF_SCANCODE | - (event.extended ? KEYEVENTF_EXTENDEDKEY : 0x0) | - (event.action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0)), - }, - }; - - UINT accepted = window_delegate_->Win32DispatchEvent(1, &input_event, - sizeof(input_event)); - if (accepted != 1) { - std::cerr << "Unable to synthesize event for keyboard event with scancode " - << event.scancode; - if (character != 0) { - std::cerr << " (character " << character << ")"; - } - std::cerr << std::endl; - } -} - void KeyboardManagerWin32::RedispatchEvent( std::unique_ptr event) { for (const Win32Message& message : event->session) { @@ -215,13 +183,11 @@ void KeyboardManagerWin32::OnKey(std::unique_ptr event, if (IsKeyUpAltRight(event->action, event->key, event->extended)) { if (should_synthesize_ctrl_left_up) { should_synthesize_ctrl_left_up = false; - PendingEvent ctrl_left_up{ - .key = VK_LCONTROL, - .scancode = ctrl_left_scancode, - .action = WM_KEYUP, - .was_down = true, - }; - DispatchEvent(ctrl_left_up); + const LPARAM lParam = + (1 /* repeat_count */ << 0) | (ctrl_left_scancode << 16) | + (0 /* extended */ << 24) | (1 /* prev_state */ << 30) | + (1 /* transition */ << 31); + window_delegate_->Win32DispatchMessage(WM_KEYUP, VK_LCONTROL, lParam); } } @@ -349,13 +315,17 @@ bool KeyboardManagerWin32::HandleMessage(UINT const action, .was_down = was_down, .session = std::move(current_session_), }); + // SYS messages must not be handled by `HandleMessage` or be + // redispatched. + const bool is_syskey = action == WM_SYSCHAR || action == WM_SYSDEADCHAR; OnKey( std::move(event), - [this, char_action = action, text]( + [this, char_action = action, text, is_syskey]( std::unique_ptr event, bool handled) { + bool real_handled = handled || is_syskey; HandleOnKeyResult(std::move(event), handled, char_action, text); }); - return true; + return !is_syskey; } // If the charcter session is not preceded by a key down message, dispatch @@ -419,12 +389,16 @@ bool KeyboardManagerWin32::HandleMessage(UINT const action, .was_down = was_down, .session = std::move(current_session_), }); + // SYS messages must not be handled by `HandleMessage` or be + // redispatched. + const bool is_syskey = action == WM_SYSKEYDOWN || action == WM_SYSKEYUP; OnKey( std::move(event), - [this](std::unique_ptr event, bool handled) { + [this, is_syskey](std::unique_ptr event, bool handled) { + bool real_handled = handled || is_syskey; HandleOnKeyResult(std::move(event), handled, 0, std::u16string()); }); - return true; + return !is_syskey; } default: assert(false); diff --git a/shell/platform/windows/keyboard_manager_win32.h b/shell/platform/windows/keyboard_manager_win32.h index c5f99b1d38585..3df408f62116b 100644 --- a/shell/platform/windows/keyboard_manager_win32.h +++ b/shell/platform/windows/keyboard_manager_win32.h @@ -75,13 +75,6 @@ class KeyboardManagerWin32 { // Used to process key messages. virtual uint32_t Win32MapVkToChar(uint32_t virtual_key) = 0; - // Win32's |SendInput|. - // - // Used to synthesize key events. - virtual UINT Win32DispatchEvent(UINT cInputs, - LPINPUT pInputs, - int cbSize) = 0; - // Win32's |SendMessage|. // // Used to synthesize key messages. @@ -92,7 +85,7 @@ class KeyboardManagerWin32 { using KeyEventCallback = WindowDelegate::KeyEventCallback; - KeyboardManagerWin32(WindowDelegate* delegate); + explicit KeyboardManagerWin32(WindowDelegate* delegate); // Processes Win32 messages related to keyboard and text. // @@ -109,8 +102,7 @@ class KeyboardManagerWin32 { bool HandleMessage(UINT const message, WPARAM const wparam, LPARAM const lparam); - - private: + protected: struct Win32Message { UINT action; WPARAM wparam; @@ -134,12 +126,11 @@ class KeyboardManagerWin32 { bool was_down; std::vector session; - - // A value calculated out of critical event information that can be used - // to identify redispatched events. - uint64_t Hash() const { return ComputeEventHash(*this); } }; + virtual void RedispatchEvent(std::unique_ptr event); + + private: using OnKeyCallback = std::function, bool)>; @@ -159,14 +150,11 @@ class KeyboardManagerWin32 { // If there's no message, returns 0. UINT PeekNextMessageType(UINT wMsgFilterMin, UINT wMsgFilterMax); - void DispatchEvent(const PendingEvent& event); - // 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 RemoveRedispatchedMessage(UINT action, WPARAM wparam, LPARAM lparam); - void RedispatchEvent(std::unique_ptr event); WindowDelegate* window_delegate_; diff --git a/shell/platform/windows/keyboard_win32_unittests.cc b/shell/platform/windows/keyboard_win32_unittests.cc index b8b66346d1862..e4feda7d25b14 100644 --- a/shell/platform/windows/keyboard_win32_unittests.cc +++ b/shell/platform/windows/keyboard_win32_unittests.cc @@ -62,13 +62,32 @@ uint32_t LayoutFrench(uint32_t virtual_key) { } } +class TestKeyboardManagerWin32 : public KeyboardManagerWin32 { + public: + explicit TestKeyboardManagerWin32(WindowDelegate* delegate) + : KeyboardManagerWin32(delegate) {} + + bool DuringRedispatch() { return during_redispatch_; } + + protected: + void RedispatchEvent(std::unique_ptr event) override { + assert(!during_redispatch_); + during_redispatch_ = true; + KeyboardManagerWin32::RedispatchEvent(std::move(event)); + during_redispatch_ = false; + } + + private: + bool during_redispatch_ = false; +}; + class MockKeyboardManagerWin32Delegate : public KeyboardManagerWin32::WindowDelegate, public MockMessageQueue { public: MockKeyboardManagerWin32Delegate(WindowBindingHandlerDelegate* view) : view_(view), map_vk_to_char_(LayoutDefault) { - keyboard_manager_ = std::make_unique(this); + keyboard_manager_ = std::make_unique(this); } virtual ~MockKeyboardManagerWin32Delegate() {} @@ -92,34 +111,6 @@ class MockKeyboardManagerWin32Delegate map_vk_to_char == nullptr ? LayoutDefault : map_vk_to_char; } - int InjectPendingEvents(uint32_t redispatch_char) { - std::vector messages; - int num_pending_responds = pending_responds_.size(); - for (const KEYBDINPUT& kbdinput : pending_responds_) { - 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); - // TODO(dkwingsmt): Don't check the message results for redispatched - // messages for now, because making them work takes non-trivial rework - // to our current structure. - // https://github.com/flutter/flutter/issues/87843 If this is resolved, - // change them to kWmResultDefault. - messages.push_back( - Win32Message{message, kbdinput.wVk, lparam, kWmResultDontCheck}); - if (redispatch_char != 0 && (kbdinput.dwFlags & KEYEVENTF_KEYUP) == 0) { - num_pending_responds += 1; - messages.push_back( - Win32Message{WM_CHAR, redispatch_char, lparam, kWmResultDontCheck}); - } - } - - pending_responds_.clear(); - InjectMessageList(messages.size(), messages.data()); - return num_pending_responds; - } - protected: BOOL Win32PeekMessage(LPMSG lpMsg, UINT wMsgFilterMin, @@ -133,6 +124,8 @@ class MockKeyboardManagerWin32Delegate return map_vk_to_char_(virtual_key); } + // This method is called for each message injected by test cases with + // `tester.InjectMessages`. LRESULT Win32SendMessage(UINT const message, WPARAM const wparam, LPARAM const lparam) override { @@ -141,28 +134,20 @@ class MockKeyboardManagerWin32Delegate : kWmResultDefault; } - UINT Win32DispatchEvent(UINT cInputs, LPINPUT pInputs, int cbSize) override { - for (UINT input_idx = 0; input_idx < cInputs; input_idx += 1) { - pending_responds_.push_back(pInputs[input_idx].ki); - } - return 1; - } - + // This method is called when the keyboard manager redispatches messages + // or dispatches CtrlLeft up for AltGr. UINT Win32DispatchMessage(UINT Msg, WPARAM wParam, LPARAM lParam) override { - pending_messages_.push_back(Win32Message{ - .message = Msg, - .wParam = wParam, - .lParam = lParam, - }); - return 1; + bool handled = keyboard_manager_->HandleMessage(Msg, wParam, lParam); + if (keyboard_manager_->DuringRedispatch()) { + EXPECT_FALSE(handled); + } + return 0; } private: WindowBindingHandlerDelegate* view_; - std::unique_ptr keyboard_manager_; + std::unique_ptr keyboard_manager_; MapVkToCharHandler map_vk_to_char_; - std::vector pending_responds_; - std::vector pending_messages_; }; class TestKeystate { @@ -334,17 +319,6 @@ class KeyboardTester { window_->InjectMessageList(count, messages); } - // Inject all events called with |SendInput| to the event queue, - // then process the event queue. - // - // Returns the number of events injected. - // - // If |redispatch_char| is not 0, then WM_KEYDOWN events will - // also redispatch a WM_CHAR event with that value as lparam. - int InjectPendingEvents(uint32_t redispatch_char = 0) { - return window_->InjectPendingEvents(redispatch_char); - } - private: std::unique_ptr view_; std::unique_ptr window_; @@ -460,9 +434,6 @@ TEST(KeyboardTest, LowerCaseAHandled) { kLogicalKeyA, "a", kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents('a'); - EXPECT_EQ(key_calls.size(), 0); - // Release A tester.InjectMessages( 1, WmKeyUpInfo{kVirtualKeyA, kScanCodeKeyA, kNotExtended}.Build( @@ -472,9 +443,6 @@ TEST(KeyboardTest, LowerCaseAHandled) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized); clear_key_calls(); - - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); } TEST(KeyboardTest, LowerCaseAUnhandled) { @@ -497,10 +465,6 @@ TEST(KeyboardTest, LowerCaseAUnhandled) { EXPECT_CALL_IS_TEXT(key_calls[1], u"a"); clear_key_calls(); - tester.InjectPendingEvents('a'); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release A tester.InjectMessages( 1, WmKeyUpInfo{kVirtualKeyA, kScanCodeKeyA, kNotExtended}.Build( @@ -510,9 +474,6 @@ TEST(KeyboardTest, LowerCaseAUnhandled) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized); clear_key_calls(); - - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); } TEST(KeyboardTest, ArrowLeftHandled) { @@ -532,9 +493,6 @@ TEST(KeyboardTest, ArrowLeftHandled) { kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 0); - EXPECT_EQ(key_calls.size(), 0); - // Release ArrowLeft tester.InjectMessages( 1, @@ -544,9 +502,6 @@ TEST(KeyboardTest, ArrowLeftHandled) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalArrowLeft, kLogicalArrowLeft, "", kNotSynthesized); clear_key_calls(); - - EXPECT_EQ(tester.InjectPendingEvents(), 0); - EXPECT_EQ(key_calls.size(), 0); } TEST(KeyboardTest, ArrowLeftUnhandled) { @@ -566,9 +521,6 @@ TEST(KeyboardTest, ArrowLeftUnhandled) { kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); - // Release ArrowLeft tester.InjectMessages( 1, @@ -578,9 +530,6 @@ TEST(KeyboardTest, ArrowLeftUnhandled) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalArrowLeft, kLogicalArrowLeft, "", kNotSynthesized); clear_key_calls(); - - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); } TEST(KeyboardTest, ShiftLeftUnhandled) { @@ -602,10 +551,6 @@ TEST(KeyboardTest, ShiftLeftUnhandled) { kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release ShiftLeft tester.SetKeyState(VK_LSHIFT, false, true); tester.InjectMessages( @@ -616,10 +561,6 @@ TEST(KeyboardTest, ShiftLeftUnhandled) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalShiftLeft, kLogicalShiftLeft, "", kNotSynthesized); clear_key_calls(); - - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); } TEST(KeyboardTest, ShiftRightUnhandled) { @@ -641,10 +582,6 @@ TEST(KeyboardTest, ShiftRightUnhandled) { kNotSynthesized); clear_key_calls(); - // Never redispatch ShiftRight. - EXPECT_EQ(tester.InjectPendingEvents(), 0); - EXPECT_EQ(key_calls.size(), 0); - // Release ShiftRight tester.SetKeyState(VK_RSHIFT, false, true); tester.InjectMessages( @@ -656,9 +593,6 @@ TEST(KeyboardTest, ShiftRightUnhandled) { kPhysicalShiftRight, kLogicalShiftRight, "", kNotSynthesized); clear_key_calls(); - - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); } TEST(KeyboardTest, CtrlLeftUnhandled) { @@ -680,10 +614,6 @@ TEST(KeyboardTest, CtrlLeftUnhandled) { kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release CtrlLeft tester.SetKeyState(VK_LCONTROL, false, true); tester.InjectMessages( @@ -695,10 +625,6 @@ TEST(KeyboardTest, CtrlLeftUnhandled) { kPhysicalControlLeft, kLogicalControlLeft, "", kNotSynthesized); clear_key_calls(); - - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); } TEST(KeyboardTest, CtrlRightUnhandled) { @@ -719,10 +645,6 @@ TEST(KeyboardTest, CtrlRightUnhandled) { kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release CtrlRight tester.SetKeyState(VK_RCONTROL, false, true); tester.InjectMessages( @@ -734,10 +656,6 @@ TEST(KeyboardTest, CtrlRightUnhandled) { kPhysicalControlRight, kLogicalControlRight, "", kNotSynthesized); clear_key_calls(); - - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); } TEST(KeyboardTest, AltLeftUnhandled) { @@ -757,11 +675,6 @@ TEST(KeyboardTest, AltLeftUnhandled) { kLogicalAltLeft, "", kNotSynthesized); clear_key_calls(); - // Sys events are not redispatched. - EXPECT_EQ(tester.InjectPendingEvents(), 0); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release AltLeft. AltLeft is a SysKeyUp event. tester.SetKeyState(VK_LMENU, false, true); tester.InjectMessages( @@ -772,11 +685,6 @@ TEST(KeyboardTest, AltLeftUnhandled) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalAltLeft, kLogicalAltLeft, "", kNotSynthesized); clear_key_calls(); - - // Sys events are not redispatched. - EXPECT_EQ(tester.InjectPendingEvents(), 0); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); } TEST(KeyboardTest, AltRightUnhandled) { @@ -797,11 +705,6 @@ TEST(KeyboardTest, AltRightUnhandled) { kNotSynthesized); clear_key_calls(); - // Sys events are not redispatched. - EXPECT_EQ(tester.InjectPendingEvents(), 0); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release AltRight. AltRight is a SysKeyUp event. tester.SetKeyState(VK_RMENU, false, true); tester.InjectMessages( @@ -812,11 +715,6 @@ TEST(KeyboardTest, AltRightUnhandled) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalAltRight, kLogicalAltRight, "", kNotSynthesized); clear_key_calls(); - - // Sys events are not redispatched. - EXPECT_EQ(tester.InjectPendingEvents(), 0); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); } TEST(KeyboardTest, MetaLeftUnhandled) { @@ -837,10 +735,6 @@ TEST(KeyboardTest, MetaLeftUnhandled) { kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release MetaLeft tester.SetKeyState(VK_LWIN, false, true); tester.InjectMessages( @@ -851,10 +745,6 @@ TEST(KeyboardTest, MetaLeftUnhandled) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalMetaLeft, kLogicalMetaLeft, "", kNotSynthesized); clear_key_calls(); - - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); } TEST(KeyboardTest, MetaRightUnhandled) { @@ -875,10 +765,6 @@ TEST(KeyboardTest, MetaRightUnhandled) { kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release MetaRight tester.SetKeyState(VK_RWIN, false, true); tester.InjectMessages( @@ -889,10 +775,6 @@ TEST(KeyboardTest, MetaRightUnhandled) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalMetaRight, kLogicalMetaRight, "", kNotSynthesized); clear_key_calls(); - - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); } // Press Shift-A. This is special because Win32 gives 'A' as character for the @@ -916,10 +798,6 @@ TEST(KeyboardTest, ShiftLeftKeyA) { kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Press A tester.InjectMessages( 2, @@ -934,10 +812,6 @@ TEST(KeyboardTest, ShiftLeftKeyA) { EXPECT_CALL_IS_TEXT(key_calls[1], u"A"); clear_key_calls(); - tester.InjectPendingEvents('A'); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release ShiftLeft tester.SetKeyState(VK_LSHIFT, false, true); tester.InjectMessages( @@ -949,10 +823,6 @@ TEST(KeyboardTest, ShiftLeftKeyA) { kLogicalShiftLeft, "", kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release A tester.InjectMessages( 1, WmKeyUpInfo{kVirtualKeyA, kScanCodeKeyA, kNotExtended}.Build( @@ -962,9 +832,6 @@ TEST(KeyboardTest, ShiftLeftKeyA) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized); clear_key_calls(); - - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); } // Press Ctrl-A. This is special because Win32 gives 0x01 as character for the @@ -988,10 +855,6 @@ TEST(KeyboardTest, CtrlLeftKeyA) { kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Press A tester.InjectMessages( 2, @@ -1005,10 +868,6 @@ TEST(KeyboardTest, CtrlLeftKeyA) { kLogicalKeyA, "", kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(0); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release A tester.InjectMessages( 1, WmKeyUpInfo{kVirtualKeyA, kScanCodeKeyA, kNotExtended}.Build( @@ -1019,9 +878,6 @@ TEST(KeyboardTest, CtrlLeftKeyA) { kLogicalKeyA, "", kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - // Release ControlLeft tester.SetKeyState(VK_LCONTROL, false, true); tester.InjectMessages( @@ -1033,10 +889,6 @@ TEST(KeyboardTest, CtrlLeftKeyA) { kPhysicalControlLeft, kLogicalControlLeft, "", kNotSynthesized); clear_key_calls(); - - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); } // Press Ctrl-1. This is special because it yields no WM_CHAR for the 1. @@ -1059,10 +911,6 @@ TEST(KeyboardTest, CtrlLeftDigit1) { kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Press 1 tester.InjectMessages( 1, WmKeyDownInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended, kWasUp} @@ -1073,10 +921,6 @@ TEST(KeyboardTest, CtrlLeftDigit1) { kLogicalDigit1, "", kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(0); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release 1 tester.InjectMessages( 1, WmKeyUpInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended}.Build( @@ -1087,9 +931,6 @@ TEST(KeyboardTest, CtrlLeftDigit1) { kLogicalDigit1, "", kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - // Release ControlLeft tester.SetKeyState(VK_LCONTROL, false, true); tester.InjectMessages( @@ -1101,10 +942,6 @@ TEST(KeyboardTest, CtrlLeftDigit1) { kPhysicalControlLeft, kLogicalControlLeft, "", kNotSynthesized); clear_key_calls(); - - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); } // Press 1 on a French keyboard. This is special because it yields WM_CHAR @@ -1129,10 +966,6 @@ TEST(KeyboardTest, Digit1OnFrenchLayout) { EXPECT_CALL_IS_TEXT(key_calls[1], u"&"); clear_key_calls(); - tester.InjectPendingEvents('&'); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release 1 tester.InjectMessages( 1, WmKeyUpInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended}.Build( @@ -1142,9 +975,6 @@ TEST(KeyboardTest, Digit1OnFrenchLayout) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalDigit1, kLogicalDigit1, "", kNotSynthesized); clear_key_calls(); - - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); } // This tests AltGr-Q on a German keyboard, which should print '@'. @@ -1172,10 +1002,6 @@ TEST(KeyboardTest, AltGrModifiedKey) { kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 2); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Press Q tester.InjectMessages( 2, @@ -1190,10 +1016,6 @@ TEST(KeyboardTest, AltGrModifiedKey) { EXPECT_CALL_IS_TEXT(key_calls[1], u"@"); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents('@'), 2); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release Q tester.InjectMessages( 1, WmKeyUpInfo{kVirtualKeyQ, kScanCodeKeyQ, kNotExtended}.Build( @@ -1204,26 +1026,18 @@ TEST(KeyboardTest, AltGrModifiedKey) { kLogicalKeyQ, "", kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); - // Release AltGr. Win32 doesn't dispatch ControlLeft up. Instead Flutter will // dispatch one. The AltGr is a system key, therefore will be handled by // Win32's default WndProc. + tester.SetKeyState(VK_LCONTROL, false, true); tester.InjectMessages( 1, WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kExtended}.Build(kWmResultDefault)); - EXPECT_EQ(key_calls.size(), 1); + EXPECT_EQ(key_calls.size(), 2); EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalAltRight, kLogicalAltRight, "", kNotSynthesized); - clear_key_calls(); - - // Dispatch the ControlLeft up event appended by Flutter. - tester.SetKeyState(VK_LCONTROL, false, true); - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 1); - EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, + EXPECT_CALL_IS_EVENT(key_calls[1], kFlutterKeyEventTypeUp, kPhysicalControlLeft, kLogicalControlLeft, "", kNotSynthesized); clear_key_calls(); @@ -1267,35 +1081,22 @@ TEST(KeyboardTest, AltGrTwice) { kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 2); - EXPECT_EQ(key_calls.size(), 0); - // 2. AltGr up. // The key up event only causes a AltRight (extended AltLeft) up. tester.SetKeyState(VK_RMENU, false, true); + tester.SetKeyState(VK_LCONTROL, false, true); tester.InjectMessages( 1, WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kExtended}.Build(kWmResultDefault)); - EXPECT_EQ(key_calls.size(), 1); + EXPECT_EQ(key_calls.size(), 2); EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalAltRight, kLogicalAltRight, "", kNotSynthesized); - clear_key_calls(); - - // Dispatch the ControlLeft up event appended by Flutter. - tester.SetKeyState(VK_LCONTROL, false, true); - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 1); - EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, + EXPECT_CALL_IS_EVENT(key_calls[1], kFlutterKeyEventTypeUp, kPhysicalControlLeft, kLogicalControlLeft, "", kNotSynthesized); clear_key_calls(); - // Redispatch the ControlLeft up event. - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // 3. AltGr down (or: ControlLeft down then AltRight down.) tester.SetKeyState(VK_LCONTROL, true, false); @@ -1315,26 +1116,18 @@ TEST(KeyboardTest, AltGrTwice) { kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 2); - EXPECT_EQ(key_calls.size(), 0); - // 4. AltGr up. // The key up event only causes a AltRight (extended AltLeft) up. tester.SetKeyState(VK_RMENU, false, false); + tester.SetKeyState(VK_LCONTROL, false, false); tester.InjectMessages( 1, WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kExtended}.Build(kWmResultDefault)); - EXPECT_EQ(key_calls.size(), 1); + EXPECT_EQ(key_calls.size(), 2); EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalAltRight, kLogicalAltRight, "", kNotSynthesized); - clear_key_calls(); - - // Dispatch a ControlLeft up event from Flutter. - tester.SetKeyState(VK_LCONTROL, false, false); - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 1); - EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, + EXPECT_CALL_IS_EVENT(key_calls[1], kFlutterKeyEventTypeUp, kPhysicalControlLeft, kLogicalControlLeft, "", kNotSynthesized); clear_key_calls(); @@ -1369,10 +1162,6 @@ TEST(KeyboardTest, DeadKeyThatCombines) { kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 0); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release ^¨ tester.InjectMessages( 1, WmKeyUpInfo{0xDD, kScanCodeBracketLeft, kNotExtended}.Build( @@ -1384,10 +1173,6 @@ TEST(KeyboardTest, DeadKeyThatCombines) { kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Press E tester.InjectMessages( 2, @@ -1402,11 +1187,6 @@ TEST(KeyboardTest, DeadKeyThatCombines) { EXPECT_CALL_IS_TEXT(key_calls[1], u"ê"); clear_key_calls(); - tester.InjectPendingEvents( - 0xEA); // The redispatched event uses unmodified 'e' - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release E tester.InjectMessages( 1, WmKeyUpInfo{kVirtualKeyE, kScanCodeKeyE, kNotExtended}.Build( @@ -1416,9 +1196,6 @@ TEST(KeyboardTest, DeadKeyThatCombines) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalKeyE, kLogicalKeyE, "", kNotSynthesized); clear_key_calls(); - - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); } // This tests dead key ^ then E on a US INTL keyboard, which should be combined @@ -1443,10 +1220,6 @@ TEST(KeyboardTest, DeadKeyWithoutDeadMaskThatCombines) { kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Press 6^ tester.InjectMessages( 2, @@ -1460,10 +1233,6 @@ TEST(KeyboardTest, DeadKeyWithoutDeadMaskThatCombines) { kLogicalDigit6, "6", kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 0); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release 6^ tester.InjectMessages( 1, WmKeyUpInfo{'6', kScanCodeDigit6, kNotExtended}.Build(kWmResultZero)); @@ -1473,10 +1242,6 @@ TEST(KeyboardTest, DeadKeyWithoutDeadMaskThatCombines) { kLogicalDigit6, "", kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release ShiftLeft tester.SetKeyState(VK_LSHIFT, false, true); tester.InjectMessages( @@ -1488,10 +1253,6 @@ TEST(KeyboardTest, DeadKeyWithoutDeadMaskThatCombines) { kLogicalShiftLeft, "", kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Press E tester.InjectMessages( 2, @@ -1506,11 +1267,6 @@ TEST(KeyboardTest, DeadKeyWithoutDeadMaskThatCombines) { EXPECT_CALL_IS_TEXT(key_calls[1], u"ê"); clear_key_calls(); - tester.InjectPendingEvents( - 0xEA); // The redispatched event uses unmodified 'e' - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release E tester.InjectMessages( 1, WmKeyUpInfo{kVirtualKeyE, kScanCodeKeyE, kNotExtended}.Build( @@ -1520,9 +1276,6 @@ TEST(KeyboardTest, DeadKeyWithoutDeadMaskThatCombines) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalKeyE, kLogicalKeyE, "", kNotSynthesized); clear_key_calls(); - - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); } // This tests dead key ^ then & (US: 1) on a French keyboard, which do not @@ -1547,10 +1300,6 @@ TEST(KeyboardTest, DeadKeyThatDoesNotCombine) { kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(0); // No WM_DEADCHAR messages sent here. - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release ^¨ tester.InjectMessages( 1, WmKeyUpInfo{0xDD, kScanCodeBracketLeft, kNotExtended}.Build( @@ -1562,10 +1311,6 @@ TEST(KeyboardTest, DeadKeyThatDoesNotCombine) { kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Press 1 tester.InjectMessages( 3, @@ -1583,10 +1328,6 @@ TEST(KeyboardTest, DeadKeyThatDoesNotCombine) { EXPECT_CALL_IS_TEXT(key_calls[2], u"&"); clear_key_calls(); - tester.InjectPendingEvents('&'); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release 1 tester.InjectMessages( 1, WmKeyUpInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended}.Build( @@ -1596,9 +1337,6 @@ TEST(KeyboardTest, DeadKeyThatDoesNotCombine) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalDigit1, kLogicalDigit1, "", kNotSynthesized); clear_key_calls(); - - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); } // This tests when the resulting character needs to be combined with surrogates. @@ -1627,20 +1365,6 @@ TEST(KeyboardTest, MultibyteCharacter) { EXPECT_CALL_IS_TEXT(key_calls[1], u"𐍅"); clear_key_calls(); - // Inject the redispatched high surrogate. - EXPECT_EQ(tester.InjectPendingEvents(0xd800), 2); - // Manually inject the redispatched low surrogate. - // - // TODO(dkwingsmt): The following message should return kWmResultZero. - // For now this is impossible since KeyboardManagerWin32 isn't passing the - // high surrogate messages to redispatching logic. - tester.InjectMessages( - 1, WmCharInfo{0xdf45, kScanCodeKeyW, kNotExtended, kWasUp}.Build( - kWmResultDontCheck)); - - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Release W tester.InjectMessages( 1, WmKeyUpInfo{kVirtualKeyW, kScanCodeKeyW, kNotExtended}.Build( @@ -1650,10 +1374,6 @@ TEST(KeyboardTest, MultibyteCharacter) { EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalKeyW, kLogicalKeyW, "", kNotSynthesized); clear_key_calls(); - - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); } // A key down event for shift right must not be redispatched even if @@ -1673,10 +1393,6 @@ TEST(KeyboardTest, NeverRedispatchShiftRightKeyDown) { EXPECT_EQ(key_calls.size(), 1); clear_key_calls(); - - // Try to dispatch events. There should be nothing. - EXPECT_EQ(tester.InjectPendingEvents(), 0); - EXPECT_EQ(key_calls.size(), 0); } // Pressing modifiers during IME events should work properly by not sending any @@ -1741,7 +1457,6 @@ TEST(KeyboardTest, DisorderlyRespondedEvents) { // Resolve the second event first to test disordered responses. recorded_callbacks.back()(false); - EXPECT_EQ(tester.InjectPendingEvents('b'), 2); EXPECT_EQ(key_calls.size(), 1); EXPECT_CALL_IS_TEXT(key_calls[0], u"b"); clear_key_calls(); @@ -1749,7 +1464,6 @@ TEST(KeyboardTest, DisorderlyRespondedEvents) { // Resolve the first event. recorded_callbacks.front()(false); - EXPECT_EQ(tester.InjectPendingEvents('a'), 2); EXPECT_EQ(key_calls.size(), 1); EXPECT_CALL_IS_TEXT(key_calls[0], u"a"); clear_key_calls(); @@ -1797,7 +1511,6 @@ TEST(KeyboardTest, SlowFrameworkResponse) { // The first response. recorded_callbacks.front()(false); - EXPECT_EQ(tester.InjectPendingEvents('a'), 2); EXPECT_EQ(key_calls.size(), 1); EXPECT_CALL_IS_TEXT(key_calls[0], u"a"); clear_key_calls(); @@ -1805,7 +1518,6 @@ TEST(KeyboardTest, SlowFrameworkResponse) { // The second response. recorded_callbacks.back()(false); - EXPECT_EQ(tester.InjectPendingEvents('a'), 2); EXPECT_EQ(key_calls.size(), 1); EXPECT_CALL_IS_TEXT(key_calls[0], u"a"); clear_key_calls(); diff --git a/shell/platform/windows/window_win32.cc b/shell/platform/windows/window_win32.cc index 16fd60116f431..aad1c1e17bb28 100644 --- a/shell/platform/windows/window_win32.cc +++ b/shell/platform/windows/window_win32.cc @@ -551,12 +551,6 @@ uint32_t WindowWin32::Win32MapVkToChar(uint32_t virtual_key) { return ::MapVirtualKey(virtual_key, MAPVK_VK_TO_CHAR); } -UINT WindowWin32::Win32DispatchEvent(UINT cInputs, - LPINPUT pInputs, - int cbSize) { - return ::SendInput(cInputs, pInputs, cbSize); -} - UINT WindowWin32::Win32DispatchMessage(UINT Msg, WPARAM wParam, LPARAM lParam) { return ::SendMessage(window_handle_, Msg, wParam, lParam); } diff --git a/shell/platform/windows/window_win32.h b/shell/platform/windows/window_win32.h index c32883c00943b..ede693ea1f172 100644 --- a/shell/platform/windows/window_win32.h +++ b/shell/platform/windows/window_win32.h @@ -48,11 +48,6 @@ class WindowWin32 : public KeyboardManagerWin32::WindowDelegate { // |KeyboardManagerWin32::WindowDelegate| virtual uint32_t Win32MapVkToChar(uint32_t virtual_key) override; - // |KeyboardManagerWin32::WindowDelegate| - virtual UINT Win32DispatchEvent(UINT cInputs, - LPINPUT pInputs, - int cbSize) override; - // |KeyboardManagerWin32::WindowDelegate| virtual UINT Win32DispatchMessage(UINT Msg, WPARAM wParam, LPARAM lParam) override; From 848b5bfd93fa8fc389cb964d6070dda06b2bd62b Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 1 Feb 2022 21:42:13 -0800 Subject: [PATCH 05/12] Fix tests. --- .../windows/keyboard_win32_unittests.cc | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/shell/platform/windows/keyboard_win32_unittests.cc b/shell/platform/windows/keyboard_win32_unittests.cc index e4feda7d25b14..d8c988f135740 100644 --- a/shell/platform/windows/keyboard_win32_unittests.cc +++ b/shell/platform/windows/keyboard_win32_unittests.cc @@ -1035,11 +1035,11 @@ TEST(KeyboardTest, AltGrModifiedKey) { WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kExtended}.Build(kWmResultDefault)); EXPECT_EQ(key_calls.size(), 2); - EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalAltRight, - kLogicalAltRight, "", kNotSynthesized); - EXPECT_CALL_IS_EVENT(key_calls[1], kFlutterKeyEventTypeUp, + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalControlLeft, kLogicalControlLeft, "", kNotSynthesized); + EXPECT_CALL_IS_EVENT(key_calls[1], kFlutterKeyEventTypeUp, kPhysicalAltRight, + kLogicalAltRight, "", kNotSynthesized); clear_key_calls(); } @@ -1090,11 +1090,11 @@ TEST(KeyboardTest, AltGrTwice) { 1, WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kExtended}.Build(kWmResultDefault)); EXPECT_EQ(key_calls.size(), 2); - EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalAltRight, - kLogicalAltRight, "", kNotSynthesized); - EXPECT_CALL_IS_EVENT(key_calls[1], kFlutterKeyEventTypeUp, + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalControlLeft, kLogicalControlLeft, "", kNotSynthesized); + EXPECT_CALL_IS_EVENT(key_calls[1], kFlutterKeyEventTypeUp, kPhysicalAltRight, + kLogicalAltRight, "", kNotSynthesized); clear_key_calls(); // 3. AltGr down (or: ControlLeft down then AltRight down.) @@ -1125,18 +1125,20 @@ TEST(KeyboardTest, AltGrTwice) { 1, WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kExtended}.Build(kWmResultDefault)); EXPECT_EQ(key_calls.size(), 2); - EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalAltRight, - kLogicalAltRight, "", kNotSynthesized); - EXPECT_CALL_IS_EVENT(key_calls[1], kFlutterKeyEventTypeUp, + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalControlLeft, kLogicalControlLeft, "", kNotSynthesized); + EXPECT_CALL_IS_EVENT(key_calls[1], kFlutterKeyEventTypeUp, kPhysicalAltRight, + kLogicalAltRight, "", kNotSynthesized); clear_key_calls(); // 5. For key sequence 2: a real ControlLeft up. tester.InjectMessages( 1, WmKeyUpInfo{VK_LCONTROL, kScanCodeControl, kNotExtended}.Build( - kWmResultDefault)); - EXPECT_EQ(key_calls.size(), 0); + kWmResultZero)); + EXPECT_EQ(key_calls.size(), 1); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeDown, 0, 0, "", + kNotSynthesized); clear_key_calls(); } From 9fae72955c84560ab160613b50d55837c404323f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 2 Feb 2022 03:38:50 -0800 Subject: [PATCH 06/12] SlowFrameworkResponseForIdenticalEvents --- .../windows/keyboard_win32_unittests.cc | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/shell/platform/windows/keyboard_win32_unittests.cc b/shell/platform/windows/keyboard_win32_unittests.cc index d8c988f135740..560f35b84486e 100644 --- a/shell/platform/windows/keyboard_win32_unittests.cc +++ b/shell/platform/windows/keyboard_win32_unittests.cc @@ -1525,6 +1525,85 @@ TEST(KeyboardTest, SlowFrameworkResponse) { clear_key_calls(); } +// Regression test for https://github.com/flutter/flutter/issues/84210. +// +// When the framework response is slow during a sequence of identical messages, +// make sure the real messages are not mistaken as redispatched messages, +// in order to not mess up the order of events. +// +// In this test we use: +// +// KeyA down, KeyA up, (down event responded with false), KeyA down, KeyA up, +// +// The code must not take the 2nd real key down events as a redispatched event. +TEST(KeyboardTest, SlowFrameworkResponseForIdenticalEvents) { + KeyboardTester tester; + + std::vector recorded_callbacks; + + // Store callbacks to manually call them. + tester.LateResponding( + [&recorded_callbacks]( + const FlutterKeyEvent* event, + MockKeyResponseController::ResponseCallback callback) { + recorded_callbacks.push_back(callback); + }); + + // Press A + tester.InjectMessages( + 2, + WmKeyDownInfo{kVirtualKeyA, kScanCodeKeyA, kNotExtended, kWasUp}.Build( + kWmResultZero), + WmCharInfo{'a', kScanCodeKeyA, kNotExtended, kWasUp}.Build( + kWmResultZero)); + + EXPECT_EQ(key_calls.size(), 1); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeDown, kPhysicalKeyA, + kLogicalKeyA, "a", kNotSynthesized); + clear_key_calls(); + + // Release A + tester.InjectMessages( + 1, WmKeyUpInfo{kVirtualKeyA, kScanCodeKeyA, kNotExtended}.Build( + kWmResultZero)); + + EXPECT_EQ(key_calls.size(), 1); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalKeyA, + kLogicalKeyA, "", kNotSynthesized); + clear_key_calls(); + + // The first down event responded with false. + EXPECT_EQ(recorded_callbacks.size(), 2); + recorded_callbacks.front()(false); + + EXPECT_EQ(key_calls.size(), 1); + EXPECT_CALL_IS_TEXT(key_calls[0], u"a"); + clear_key_calls(); + + // Press A again + tester.InjectMessages( + 2, + WmKeyDownInfo{kVirtualKeyA, kScanCodeKeyA, kNotExtended, kWasUp}.Build( + kWmResultZero), + WmCharInfo{'a', kScanCodeKeyA, kNotExtended, kWasUp}.Build( + kWmResultZero)); + + EXPECT_EQ(key_calls.size(), 1); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeDown, kPhysicalKeyA, + kLogicalKeyA, "a", kNotSynthesized); + clear_key_calls(); + + // Release A again + tester.InjectMessages( + 1, WmKeyUpInfo{kVirtualKeyA, kScanCodeKeyA, kNotExtended}.Build( + kWmResultZero)); + + EXPECT_EQ(key_calls.size(), 1); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalKeyA, + kLogicalKeyA, "", kNotSynthesized); + clear_key_calls(); +} + TEST(KeyboardTest, TextInputSubmit) { KeyboardTester tester; tester.Responding(false); From 24fb37b6b89ea199ff57fa68100594d76aec00c5 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 2 Feb 2022 04:37:05 -0800 Subject: [PATCH 07/12] Test; unfinished --- .../windows/keyboard_manager_win32.cc | 1 - .../windows/keyboard_win32_unittests.cc | 85 ++++++++++++++++++- shell/platform/windows/testing/wm_builders.cc | 6 +- shell/platform/windows/testing/wm_builders.h | 3 + 4 files changed, 90 insertions(+), 5 deletions(-) diff --git a/shell/platform/windows/keyboard_manager_win32.cc b/shell/platform/windows/keyboard_manager_win32.cc index 6334715b030ed..41b8342f90fc4 100644 --- a/shell/platform/windows/keyboard_manager_win32.cc +++ b/shell/platform/windows/keyboard_manager_win32.cc @@ -287,7 +287,6 @@ bool KeyboardManagerWin32::HandleMessage(UINT const action, // OnText if the key down event is not handled. if (current_session_.front().IsGeneralKeyDown()) { const Win32Message first_message = current_session_.front(); - current_session_.clear(); const uint8_t scancode = (lparam >> 16) & 0xff; const uint16_t key_code = first_message.wparam; const bool extended = ((lparam >> 24) & 0x01) == 0x01; diff --git a/shell/platform/windows/keyboard_win32_unittests.cc b/shell/platform/windows/keyboard_win32_unittests.cc index 560f35b84486e..af6424d153c69 100644 --- a/shell/platform/windows/keyboard_win32_unittests.cc +++ b/shell/platform/windows/keyboard_win32_unittests.cc @@ -91,7 +91,7 @@ class MockKeyboardManagerWin32Delegate } virtual ~MockKeyboardManagerWin32Delegate() {} - // |WindowWin32| + // |KeyboardManagerWin32::WindowDelegate| void OnKey(int key, int scancode, int action, @@ -103,7 +103,7 @@ class MockKeyboardManagerWin32Delegate callback); } - // |WindowWin32| + // |KeyboardManagerWin32::WindowDelegate| void OnText(const std::u16string& text) override { view_->OnText(text); } void SetLayout(MapVkToCharHandler map_vk_to_char) { @@ -369,6 +369,7 @@ class KeyboardTester { } }; +constexpr uint64_t kScanCodeBackquote = 0x29; constexpr uint64_t kScanCodeKeyA = 0x1e; constexpr uint64_t kScanCodeKeyB = 0x30; constexpr uint64_t kScanCodeKeyE = 0x12; @@ -1341,6 +1342,86 @@ TEST(KeyboardTest, DeadKeyThatDoesNotCombine) { clear_key_calls(); } +// This tests dead key `, then dead key `, then e. +// +// It should output ``e, instead of `è. +TEST(KeyboardTest, DeadKeyTwiceThenLetter) { + KeyboardTester tester; + tester.Responding(false); + + // US INTL layout. + + // Press ` + tester.InjectMessages( + 2, + WmKeyDownInfo{0xC0, kScanCodeBackquote, kNotExtended, kWasUp}.Build( + kWmResultZero), + WmDeadCharInfo{'`', kScanCodeBackquote, kNotExtended, kWasUp}.Build( + kWmResultZero)); + + EXPECT_EQ(key_calls.size(), 1); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeDown, + kPhysicalBackquote, kLogicalBackquote, "`", + kNotSynthesized); + clear_key_calls(); + + // Release ` + tester.InjectMessages( + 1, WmKeyUpInfo{0xC0, kScanCodeBackquote, kNotExtended}.Build( + kWmResultZero)); + + EXPECT_EQ(key_calls.size(), 1); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, + kPhysicalBackquote, kLogicalBackquote, "", + kNotSynthesized); + clear_key_calls(); + + // Press ` again. + // The response should be slow. + std::vector recorded_callbacks; + tester.LateResponding( + [&recorded_callbacks]( + const FlutterKeyEvent* event, + MockKeyResponseController::ResponseCallback callback) { + recorded_callbacks.push_back(callback); + }); + + tester.InjectMessages( + 3, + WmKeyDownInfo{0xC0, kScanCodeBackquote, kNotExtended, kWasUp}.Build( + kWmResultZero), + WmCharInfo{'`', kScanCodeBackquote, kNotExtended, kWasUp, + kBeingReleased, kNoContext, 1, /*bit25*/ true} + .Build(kWmResultZero), + WmCharInfo{'`', kScanCodeBackquote, kNotExtended, kWasUp}.Build( + kWmResultZero)); + + EXPECT_EQ(recorded_callbacks.size(), 1); + EXPECT_EQ(key_calls.size(), 2); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeDown, kPhysicalBackquote, + kLogicalBackquote, "`", kNotSynthesized); + EXPECT_CALL_IS_TEXT(key_calls[1], u"`"); + clear_key_calls(); + // Key down event responded with false. + recorded_callbacks.front()(false); + EXPECT_EQ(key_calls.size(), 1); + EXPECT_CALL_IS_TEXT(key_calls[0], u"`"); + clear_key_calls(); + + tester.Responding(false); + + // Release ` + tester.InjectMessages( + 1, WmKeyUpInfo{0xC0, kScanCodeBackquote, kNotExtended}.Build( + kWmResultZero)); + + EXPECT_EQ(key_calls.size(), 1); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, + kPhysicalBackquote, kLogicalBackquote, "", + kNotSynthesized); + clear_key_calls(); +} + // This tests when the resulting character needs to be combined with surrogates. TEST(KeyboardTest, MultibyteCharacter) { KeyboardTester tester; diff --git a/shell/platform/windows/testing/wm_builders.cc b/shell/platform/windows/testing/wm_builders.cc index 0d0aea777ca72..1db0db5a696e5 100644 --- a/shell/platform/windows/testing/wm_builders.cc +++ b/shell/platform/windows/testing/wm_builders.cc @@ -34,7 +34,8 @@ Win32Message WmKeyUpInfo::Build(LRESULT expected_result, HWND hWnd) { Win32Message WmCharInfo::Build(LRESULT expected_result, HWND hWnd) { uint32_t lParam = (repeat_count << 0) | (scan_code << 16) | (extended << 24) | - (context << 30) | (prev_state << 30) | (transition << 31); + (bit25 << 25) | (context << 29) | (prev_state << 30) | + (transition << 31); return Win32Message{ .message = WM_CHAR, .wParam = char_code, @@ -72,7 +73,8 @@ Win32Message WmSysKeyUpInfo::Build(LRESULT expected_result, HWND hWnd) { Win32Message WmDeadCharInfo::Build(LRESULT expected_result, HWND hWnd) { uint32_t lParam = (repeat_count << 0) | (scan_code << 16) | (extended << 24) | - (context << 30) | (prev_state << 30) | (transition << 31); + (context << 30) | (prev_state << 30) | + (transition << 31); return Win32Message{ .message = WM_DEADCHAR, .wParam = char_code, diff --git a/shell/platform/windows/testing/wm_builders.h b/shell/platform/windows/testing/wm_builders.h index 747d7075f6393..c4af592e33f47 100644 --- a/shell/platform/windows/testing/wm_builders.h +++ b/shell/platform/windows/testing/wm_builders.h @@ -111,6 +111,9 @@ typedef struct WmCharInfo { uint16_t repeat_count = 1; + // Some messages are sent with bit25. It's meaning is unknown. + bool bit25 = 0; + Win32Message Build(LRESULT expected_result = kWmResultDontCheck, HWND hWnd = NULL); } WmCharInfo; From a5fa4c5652de6cb2f31d2429b87c51811fa82ff2 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 2 Feb 2022 13:22:28 -0800 Subject: [PATCH 08/12] Fix text order --- .../windows/keyboard_manager_win32.cc | 36 +++++++++++++++---- .../platform/windows/keyboard_manager_win32.h | 31 +++++++++++++--- .../windows/keyboard_win32_unittests.cc | 12 +++---- 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/shell/platform/windows/keyboard_manager_win32.cc b/shell/platform/windows/keyboard_manager_win32.cc index 41b8342f90fc4..cc13407a48ed5 100644 --- a/shell/platform/windows/keyboard_manager_win32.cc +++ b/shell/platform/windows/keyboard_manager_win32.cc @@ -201,11 +201,19 @@ void KeyboardManagerWin32::OnKey(std::unique_ptr event, }); } +void KeyboardManagerWin32::DispatchReadyTexts() { + auto front = pending_texts_.begin(); + for (; front != pending_texts_.end() && front->ready; ++front) { + window_delegate_->OnText(front->content); + } + pending_texts_.erase(pending_texts_.begin(), front); +} + void KeyboardManagerWin32::HandleOnKeyResult( std::unique_ptr event, bool handled, int char_action, - std::u16string text) { + std::list::iterator pending_text) { // First, patch |handled|, because some key events must always be treated as // handled. // @@ -223,6 +231,9 @@ void KeyboardManagerWin32::HandleOnKeyResult( // For handled events, that's all. if (real_handled) { + if (pending_text != pending_texts_.end()) { + pending_texts_.erase(pending_text); + } return; } @@ -233,8 +244,10 @@ void KeyboardManagerWin32::HandleOnKeyResult( // will be incorporated into a later WM_CHAR with the full character. // Non-printable event characters have been filtered out before being passed // to OnKey. - if (char_action == WM_CHAR && event->character != 0) { - window_delegate_->OnText(text); + if (char_action == WM_CHAR && event->character != 0 && + pending_text != pending_texts_.end()) { + pending_text->ready = true; + DispatchReadyTexts(); } RedispatchEvent(std::move(event)); @@ -314,15 +327,20 @@ bool KeyboardManagerWin32::HandleMessage(UINT const action, .was_down = was_down, .session = std::move(current_session_), }); + pending_texts_.push_back(PendingText{ + .ready = false, + .content = text, + }); + auto pending_text = std::prev(pending_texts_.end()); // SYS messages must not be handled by `HandleMessage` or be // redispatched. const bool is_syskey = action == WM_SYSCHAR || action == WM_SYSDEADCHAR; OnKey( std::move(event), - [this, char_action = action, text, is_syskey]( + [this, char_action = action, pending_text, is_syskey]( std::unique_ptr event, bool handled) { bool real_handled = handled || is_syskey; - HandleOnKeyResult(std::move(event), handled, char_action, text); + HandleOnKeyResult(std::move(event), handled, char_action, pending_text); }); return !is_syskey; } @@ -338,7 +356,11 @@ bool KeyboardManagerWin32::HandleMessage(UINT const action, // events for all control key shortcuts. current_session_.clear(); if (action == WM_CHAR && IsPrintable(wparam)) { - window_delegate_->OnText(text); + pending_texts_.push_back(PendingText{ + .ready = true, + .content = text, + }); + DispatchReadyTexts(); } return true; } @@ -395,7 +417,7 @@ bool KeyboardManagerWin32::HandleMessage(UINT const action, std::move(event), [this, is_syskey](std::unique_ptr event, bool handled) { bool real_handled = handled || is_syskey; - HandleOnKeyResult(std::move(event), handled, 0, std::u16string()); + HandleOnKeyResult(std::move(event), handled, 0, pending_texts_.end()); }); return !is_syskey; } diff --git a/shell/platform/windows/keyboard_manager_win32.h b/shell/platform/windows/keyboard_manager_win32.h index 3df408f62116b..10da903d2753f 100644 --- a/shell/platform/windows/keyboard_manager_win32.h +++ b/shell/platform/windows/keyboard_manager_win32.h @@ -134,13 +134,27 @@ class KeyboardManagerWin32 { using OnKeyCallback = std::function, bool)>; + struct PendingText { + bool ready; + std::u16string content; + }; + // Returns true if it's a new event, or false if it's a redispatched event. void OnKey(std::unique_ptr event, OnKeyCallback callback); + // From `pending_texts_`, pop all front elements that are ready, dispatch + // them to |OnText|, and remove them. + void DispatchReadyTexts(); + + // Handle the result of |OnKey|, which might dispatch the text result to + // |OnText|. + // + // The `pending_text` is either a valid iterator of `pending_texts`, or its + // end(). In the latter case, this OnKey message does not contain a text. void HandleOnKeyResult(std::unique_ptr event, bool handled, int char_action, - std::u16string text); + std::list::iterator pending_text); // Returns the type of the next WM message. // @@ -159,10 +173,11 @@ class KeyboardManagerWin32 { WindowDelegate* window_delegate_; // Keeps track of all messages during the current session. + // + // At the end of a session, it is moved to the `PendingEvent`, which is + // passed to `OnKey`. std::vector current_session_; - std::map text_for_scancode_on_redispatch_; - // Whether the last event is a CtrlLeft key down. // // This is used to resolve a corner case described in |IsKeyDownAltRight|. @@ -178,7 +193,15 @@ class KeyboardManagerWin32 { // This is used to resolve a corner case described in |IsKeyDownAltRight|. bool should_synthesize_ctrl_left_up; - // The queue of key events that have been redispatched to the system but have + // A queue of potential texts derived from char messages. + // + // The text might or might not be ready when they're added, and they might + // become ready or removed later. `DispatchReadyTexts` is used to dispatch all + // ready texts from the front to `OnText`. This queue is used to ensure + // they're dispatched in their arrival order. + std::list pending_texts_; + + // The queue of messages that have been redispatched to the system but have // not yet been received for a second time. std::deque pending_redispatches_; diff --git a/shell/platform/windows/keyboard_win32_unittests.cc b/shell/platform/windows/keyboard_win32_unittests.cc index af6424d153c69..5d3466af23fac 100644 --- a/shell/platform/windows/keyboard_win32_unittests.cc +++ b/shell/platform/windows/keyboard_win32_unittests.cc @@ -1397,15 +1397,15 @@ TEST(KeyboardTest, DeadKeyTwiceThenLetter) { kWmResultZero)); EXPECT_EQ(recorded_callbacks.size(), 1); - EXPECT_EQ(key_calls.size(), 2); + EXPECT_EQ(key_calls.size(), 1); EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeDown, kPhysicalBackquote, kLogicalBackquote, "`", kNotSynthesized); - EXPECT_CALL_IS_TEXT(key_calls[1], u"`"); clear_key_calls(); // Key down event responded with false. recorded_callbacks.front()(false); - EXPECT_EQ(key_calls.size(), 1); + EXPECT_EQ(key_calls.size(), 2); EXPECT_CALL_IS_TEXT(key_calls[0], u"`"); + EXPECT_CALL_IS_TEXT(key_calls[1], u"`"); clear_key_calls(); tester.Responding(false); @@ -1540,15 +1540,15 @@ TEST(KeyboardTest, DisorderlyRespondedEvents) { // Resolve the second event first to test disordered responses. recorded_callbacks.back()(false); - EXPECT_EQ(key_calls.size(), 1); - EXPECT_CALL_IS_TEXT(key_calls[0], u"b"); + EXPECT_EQ(key_calls.size(), 0); clear_key_calls(); // Resolve the first event. recorded_callbacks.front()(false); - EXPECT_EQ(key_calls.size(), 1); + EXPECT_EQ(key_calls.size(), 2); EXPECT_CALL_IS_TEXT(key_calls[0], u"a"); + EXPECT_CALL_IS_TEXT(key_calls[1], u"b"); clear_key_calls(); } From b69cbda8b8de4e0757be7988f17d2f3c7efa7619 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 2 Feb 2022 13:28:15 -0800 Subject: [PATCH 09/12] 25bit --- shell/platform/windows/testing/wm_builders.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shell/platform/windows/testing/wm_builders.h b/shell/platform/windows/testing/wm_builders.h index c4af592e33f47..4502394368fa6 100644 --- a/shell/platform/windows/testing/wm_builders.h +++ b/shell/platform/windows/testing/wm_builders.h @@ -111,7 +111,9 @@ typedef struct WmCharInfo { uint16_t repeat_count = 1; - // Some messages are sent with bit25. It's meaning is unknown. + // The 25th bit of the LParam. + // + // Some messages are sent bit25 set. It's meaning is yet unknown. bool bit25 = 0; Win32Message Build(LRESULT expected_result = kWmResultDontCheck, From 2d68a615f79cbc897b50c33afe7468c411dd79ec Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 2 Feb 2022 13:29:23 -0800 Subject: [PATCH 10/12] Format --- .../windows/keyboard_manager_win32.cc | 34 +++++++++---------- .../platform/windows/keyboard_manager_win32.h | 3 +- .../windows/keyboard_win32_unittests.cc | 27 +++++++-------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/shell/platform/windows/keyboard_manager_win32.cc b/shell/platform/windows/keyboard_manager_win32.cc index cc13407a48ed5..d1964f18fd27d 100644 --- a/shell/platform/windows/keyboard_manager_win32.cc +++ b/shell/platform/windows/keyboard_manager_win32.cc @@ -328,20 +328,20 @@ bool KeyboardManagerWin32::HandleMessage(UINT const action, .session = std::move(current_session_), }); pending_texts_.push_back(PendingText{ - .ready = false, - .content = text, + .ready = false, + .content = text, }); auto pending_text = std::prev(pending_texts_.end()); // SYS messages must not be handled by `HandleMessage` or be // redispatched. const bool is_syskey = action == WM_SYSCHAR || action == WM_SYSDEADCHAR; - OnKey( - std::move(event), - [this, char_action = action, pending_text, is_syskey]( - std::unique_ptr event, bool handled) { - bool real_handled = handled || is_syskey; - HandleOnKeyResult(std::move(event), handled, char_action, pending_text); - }); + OnKey(std::move(event), + [this, char_action = action, pending_text, is_syskey]( + std::unique_ptr event, bool handled) { + bool real_handled = handled || is_syskey; + HandleOnKeyResult(std::move(event), handled, char_action, + pending_text); + }); return !is_syskey; } @@ -357,8 +357,8 @@ bool KeyboardManagerWin32::HandleMessage(UINT const action, current_session_.clear(); if (action == WM_CHAR && IsPrintable(wparam)) { pending_texts_.push_back(PendingText{ - .ready = true, - .content = text, + .ready = true, + .content = text, }); DispatchReadyTexts(); } @@ -413,12 +413,12 @@ bool KeyboardManagerWin32::HandleMessage(UINT const action, // SYS messages must not be handled by `HandleMessage` or be // redispatched. const bool is_syskey = action == WM_SYSKEYDOWN || action == WM_SYSKEYUP; - OnKey( - std::move(event), - [this, is_syskey](std::unique_ptr event, bool handled) { - bool real_handled = handled || is_syskey; - HandleOnKeyResult(std::move(event), handled, 0, pending_texts_.end()); - }); + OnKey(std::move(event), [this, is_syskey]( + std::unique_ptr event, + bool handled) { + bool real_handled = handled || is_syskey; + HandleOnKeyResult(std::move(event), handled, 0, pending_texts_.end()); + }); return !is_syskey; } default: diff --git a/shell/platform/windows/keyboard_manager_win32.h b/shell/platform/windows/keyboard_manager_win32.h index 10da903d2753f..e9dc64d06527e 100644 --- a/shell/platform/windows/keyboard_manager_win32.h +++ b/shell/platform/windows/keyboard_manager_win32.h @@ -102,6 +102,7 @@ class KeyboardManagerWin32 { bool HandleMessage(UINT const message, WPARAM const wparam, LPARAM const lparam); + protected: struct Win32Message { UINT action; @@ -128,7 +129,7 @@ class KeyboardManagerWin32 { std::vector session; }; - virtual void RedispatchEvent(std::unique_ptr event); + virtual void RedispatchEvent(std::unique_ptr event); private: using OnKeyCallback = diff --git a/shell/platform/windows/keyboard_win32_unittests.cc b/shell/platform/windows/keyboard_win32_unittests.cc index 5d3466af23fac..fdd053c77618c 100644 --- a/shell/platform/windows/keyboard_win32_unittests.cc +++ b/shell/platform/windows/keyboard_win32_unittests.cc @@ -1367,13 +1367,12 @@ TEST(KeyboardTest, DeadKeyTwiceThenLetter) { // Release ` tester.InjectMessages( - 1, WmKeyUpInfo{0xC0, kScanCodeBackquote, kNotExtended}.Build( - kWmResultZero)); + 1, + WmKeyUpInfo{0xC0, kScanCodeBackquote, kNotExtended}.Build(kWmResultZero)); EXPECT_EQ(key_calls.size(), 1); - EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, - kPhysicalBackquote, kLogicalBackquote, "", - kNotSynthesized); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalBackquote, + kLogicalBackquote, "", kNotSynthesized); clear_key_calls(); // Press ` again. @@ -1390,16 +1389,17 @@ TEST(KeyboardTest, DeadKeyTwiceThenLetter) { 3, WmKeyDownInfo{0xC0, kScanCodeBackquote, kNotExtended, kWasUp}.Build( kWmResultZero), - WmCharInfo{'`', kScanCodeBackquote, kNotExtended, kWasUp, - kBeingReleased, kNoContext, 1, /*bit25*/ true} + WmCharInfo{'`', kScanCodeBackquote, kNotExtended, kWasUp, kBeingReleased, + kNoContext, 1, /*bit25*/ true} .Build(kWmResultZero), WmCharInfo{'`', kScanCodeBackquote, kNotExtended, kWasUp}.Build( kWmResultZero)); EXPECT_EQ(recorded_callbacks.size(), 1); EXPECT_EQ(key_calls.size(), 1); - EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeDown, kPhysicalBackquote, - kLogicalBackquote, "`", kNotSynthesized); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeDown, + kPhysicalBackquote, kLogicalBackquote, "`", + kNotSynthesized); clear_key_calls(); // Key down event responded with false. recorded_callbacks.front()(false); @@ -1412,13 +1412,12 @@ TEST(KeyboardTest, DeadKeyTwiceThenLetter) { // Release ` tester.InjectMessages( - 1, WmKeyUpInfo{0xC0, kScanCodeBackquote, kNotExtended}.Build( - kWmResultZero)); + 1, + WmKeyUpInfo{0xC0, kScanCodeBackquote, kNotExtended}.Build(kWmResultZero)); EXPECT_EQ(key_calls.size(), 1); - EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, - kPhysicalBackquote, kLogicalBackquote, "", - kNotSynthesized); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalBackquote, + kLogicalBackquote, "", kNotSynthesized); clear_key_calls(); } From 199a971b1a00045de51b0120a7d4da0bffe882ee Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 2 Feb 2022 13:57:15 -0800 Subject: [PATCH 11/12] Format --- shell/platform/windows/flutter_window_win32_unittests.cc | 2 +- shell/platform/windows/testing/wm_builders.cc | 3 +-- shell/platform/windows/window_win32.h | 4 +++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/shell/platform/windows/flutter_window_win32_unittests.cc b/shell/platform/windows/flutter_window_win32_unittests.cc index 250071c862291..007e5fc791657 100644 --- a/shell/platform/windows/flutter_window_win32_unittests.cc +++ b/shell/platform/windows/flutter_window_win32_unittests.cc @@ -126,7 +126,7 @@ class MockFlutterWindowWin32 : public FlutterWindowWin32 { MOCK_METHOD0(OnResetImeComposing, void()); MOCK_METHOD3(Win32DispatchMessage, UINT(UINT, WPARAM, LPARAM)); MOCK_METHOD4(Win32PeekMessage, BOOL(LPMSG, UINT, UINT, UINT)); - MOCK_METHOD1(Win32MapVkToChar, uint32_t(uint32_t )); + MOCK_METHOD1(Win32MapVkToChar, uint32_t(uint32_t)); protected: // |KeyboardManagerWin32::WindowDelegate| diff --git a/shell/platform/windows/testing/wm_builders.cc b/shell/platform/windows/testing/wm_builders.cc index 1db0db5a696e5..396aca2ddd069 100644 --- a/shell/platform/windows/testing/wm_builders.cc +++ b/shell/platform/windows/testing/wm_builders.cc @@ -73,8 +73,7 @@ Win32Message WmSysKeyUpInfo::Build(LRESULT expected_result, HWND hWnd) { Win32Message WmDeadCharInfo::Build(LRESULT expected_result, HWND hWnd) { uint32_t lParam = (repeat_count << 0) | (scan_code << 16) | (extended << 24) | - (context << 30) | (prev_state << 30) | - (transition << 31); + (context << 30) | (prev_state << 30) | (transition << 31); return Win32Message{ .message = WM_DEADCHAR, .wParam = char_code, diff --git a/shell/platform/windows/window_win32.h b/shell/platform/windows/window_win32.h index ede693ea1f172..4c30275fcf37d 100644 --- a/shell/platform/windows/window_win32.h +++ b/shell/platform/windows/window_win32.h @@ -49,7 +49,9 @@ class WindowWin32 : public KeyboardManagerWin32::WindowDelegate { virtual uint32_t Win32MapVkToChar(uint32_t virtual_key) override; // |KeyboardManagerWin32::WindowDelegate| - virtual UINT Win32DispatchMessage(UINT Msg, WPARAM wParam, LPARAM lParam) override; + virtual UINT Win32DispatchMessage(UINT Msg, + WPARAM wParam, + LPARAM lParam) override; protected: // Converts a c string to a wide unicode string. From e897aea295b55d597b36cbf25a756404c4169395 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 2 Feb 2022 14:27:19 -0800 Subject: [PATCH 12/12] typo fixes --- shell/platform/windows/keyboard_manager_win32.cc | 3 +-- shell/platform/windows/testing/wm_builders.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/shell/platform/windows/keyboard_manager_win32.cc b/shell/platform/windows/keyboard_manager_win32.cc index d1964f18fd27d..86aede8da48b1 100644 --- a/shell/platform/windows/keyboard_manager_win32.cc +++ b/shell/platform/windows/keyboard_manager_win32.cc @@ -136,9 +136,8 @@ KeyboardManagerWin32::KeyboardManagerWin32(WindowDelegate* delegate) void KeyboardManagerWin32::RedispatchEvent( std::unique_ptr event) { for (const Win32Message& message : event->session) { - UINT result = 0; pending_redispatches_.push_back(message); - result = window_delegate_->Win32DispatchMessage( + UINT result = window_delegate_->Win32DispatchMessage( message.action, message.wparam, message.lparam); if (result != 0) { std::cerr << "Unable to synthesize event for keyboard event." diff --git a/shell/platform/windows/testing/wm_builders.h b/shell/platform/windows/testing/wm_builders.h index 4502394368fa6..fd3b8914388db 100644 --- a/shell/platform/windows/testing/wm_builders.h +++ b/shell/platform/windows/testing/wm_builders.h @@ -113,7 +113,7 @@ typedef struct WmCharInfo { // The 25th bit of the LParam. // - // Some messages are sent bit25 set. It's meaning is yet unknown. + // Some messages are sent with bit25 set. Its meaning is yet unknown. bool bit25 = 0; Win32Message Build(LRESULT expected_result = kWmResultDontCheck,