diff --git a/shell/platform/windows/flutter_window_win32_unittests.cc b/shell/platform/windows/flutter_window_win32_unittests.cc index b93d5b85807a9..007e5fc791657 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 ba9014ca78728..86aede8da48b1 100644 --- a/shell/platform/windows/keyboard_manager_win32.cc +++ b/shell/platform/windows/keyboard_manager_win32.cc @@ -133,55 +133,31 @@ 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) { - DispatchEvent(*event); + for (const Win32Message& message : event->session) { + pending_redispatches_.push_back(message); + UINT result = window_delegate_->Win32DispatchMessage( + message.action, message.wparam, message.lparam); + if (result != 0) { + std::cerr << "Unable to synthesize event for keyboard event." + << 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 && wparam == iter->wparam) { pending_redispatches_.erase(iter); return true; } @@ -189,12 +165,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; @@ -210,13 +182,11 @@ bool 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); } } @@ -228,14 +198,21 @@ bool KeyboardManagerWin32::OnKey(std::unique_ptr event, callback(std::unique_ptr(event), handled); }); - return true; +} + +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. // @@ -253,6 +230,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; } @@ -263,8 +243,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)); @@ -273,6 +255,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: @@ -314,7 +299,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; @@ -342,16 +326,22 @@ bool KeyboardManagerWin32::HandleMessage(UINT const action, .was_down = was_down, .session = std::move(current_session_), }); - const bool is_unmet_event = 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; + 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, 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; } // If the charcter session is not preceded by a key down message, dispatch @@ -365,7 +355,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; } @@ -415,15 +409,16 @@ bool KeyboardManagerWin32::HandleMessage(UINT const action, .was_down = was_down, .session = std::move(current_session_), }); - const bool is_unmet_event = OnKey( - std::move(event), - [this](std::unique_ptr event, bool handled) { - HandleOnKeyResult(std::move(event), handled, 0, std::u16string()); - }); + // SYS messages must not be handled by `HandleMessage` or be + // redispatched. 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; + 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: assert(false); diff --git a/shell/platform/windows/keyboard_manager_win32.h b/shell/platform/windows/keyboard_manager_win32.h index 8cd1dd49a81d1..e9dc64d06527e 100644 --- a/shell/platform/windows/keyboard_manager_win32.h +++ b/shell/platform/windows/keyboard_manager_win32.h @@ -75,17 +75,17 @@ class KeyboardManagerWin32 { // Used to process key messages. virtual uint32_t Win32MapVkToChar(uint32_t virtual_key) = 0; - // Win32's |SendInput|. + // Win32's |SendMessage|. // - // Used to synthesize key events. - virtual UINT Win32DispatchEvent(UINT cInputs, - LPINPUT pInputs, - int cbSize) = 0; + // Used to synthesize key messages. + virtual UINT Win32DispatchMessage(UINT Msg, + WPARAM wParam, + LPARAM lParam) = 0; }; using KeyEventCallback = WindowDelegate::KeyEventCallback; - KeyboardManagerWin32(WindowDelegate* delegate); + explicit KeyboardManagerWin32(WindowDelegate* delegate); // Processes Win32 messages related to keyboard and text. // @@ -103,11 +103,11 @@ class KeyboardManagerWin32 { WPARAM const wparam, LPARAM const lparam); - private: + protected: 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); } @@ -127,22 +127,35 @@ 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)>; + struct PendingText { + bool ready; + std::u16string content; + }; + // 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); + // 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. // @@ -152,22 +165,20 @@ 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 RemoveRedispatchedEvent(const PendingEvent& incoming); - void RedispatchEvent(std::unique_ptr event); + bool RemoveRedispatchedMessage(UINT action, WPARAM wparam, LPARAM lparam); 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|. @@ -183,9 +194,17 @@ 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_; + 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..fdd053c77618c 100644 --- a/shell/platform/windows/keyboard_win32_unittests.cc +++ b/shell/platform/windows/keyboard_win32_unittests.cc @@ -62,17 +62,36 @@ 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() {} - // |WindowWin32| + // |KeyboardManagerWin32::WindowDelegate| void OnKey(int key, int scancode, int action, @@ -84,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) { @@ -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,18 +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); + // 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 { + bool handled = keyboard_manager_->HandleMessage(Msg, wParam, lParam); + if (keyboard_manager_->DuringRedispatch()) { + EXPECT_FALSE(handled); } - return 1; + return 0; } private: WindowBindingHandlerDelegate* view_; - std::unique_ptr keyboard_manager_; + std::unique_ptr keyboard_manager_; MapVkToCharHandler map_vk_to_char_; - std::vector pending_responds_; }; class TestKeystate { @@ -324,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_; @@ -385,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; @@ -450,9 +435,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( @@ -462,9 +444,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) { @@ -487,10 +466,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( @@ -500,9 +475,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) { @@ -522,9 +494,6 @@ TEST(KeyboardTest, ArrowLeftHandled) { kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 0); - EXPECT_EQ(key_calls.size(), 0); - // Release ArrowLeft tester.InjectMessages( 1, @@ -534,9 +503,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) { @@ -556,9 +522,6 @@ TEST(KeyboardTest, ArrowLeftUnhandled) { kNotSynthesized); clear_key_calls(); - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); - // Release ArrowLeft tester.InjectMessages( 1, @@ -568,9 +531,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) { @@ -592,10 +552,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( @@ -606,10 +562,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) { @@ -631,10 +583,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( @@ -646,9 +594,6 @@ TEST(KeyboardTest, ShiftRightUnhandled) { kPhysicalShiftRight, kLogicalShiftRight, "", kNotSynthesized); clear_key_calls(); - - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); } TEST(KeyboardTest, CtrlLeftUnhandled) { @@ -670,10 +615,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( @@ -685,10 +626,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) { @@ -709,10 +646,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( @@ -724,10 +657,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) { @@ -747,11 +676,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( @@ -762,11 +686,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) { @@ -787,11 +706,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( @@ -802,11 +716,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) { @@ -827,10 +736,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( @@ -841,10 +746,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) { @@ -865,10 +766,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( @@ -879,10 +776,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 @@ -906,10 +799,6 @@ TEST(KeyboardTest, ShiftLeftKeyA) { kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Press A tester.InjectMessages( 2, @@ -924,10 +813,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( @@ -939,10 +824,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( @@ -952,9 +833,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 @@ -978,10 +856,6 @@ TEST(KeyboardTest, CtrlLeftKeyA) { kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Press A tester.InjectMessages( 2, @@ -995,10 +869,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( @@ -1009,9 +879,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( @@ -1023,10 +890,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. @@ -1049,10 +912,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} @@ -1063,10 +922,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( @@ -1077,9 +932,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( @@ -1091,10 +943,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 @@ -1119,10 +967,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( @@ -1132,9 +976,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 '@'. @@ -1162,10 +1003,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, @@ -1180,10 +1017,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( @@ -1194,28 +1027,20 @@ 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_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_EQ(key_calls.size(), 2); 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(); } @@ -1257,33 +1082,20 @@ 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_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_EQ(key_calls.size(), 2); EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeUp, kPhysicalControlLeft, kLogicalControlLeft, "", kNotSynthesized); - clear_key_calls(); - - // Redispatch the ControlLeft up event. - EXPECT_EQ(tester.InjectPendingEvents(), 1); - EXPECT_EQ(key_calls.size(), 0); + EXPECT_CALL_IS_EVENT(key_calls[1], kFlutterKeyEventTypeUp, kPhysicalAltRight, + kLogicalAltRight, "", kNotSynthesized); clear_key_calls(); // 3. AltGr down (or: ControlLeft down then AltRight down.) @@ -1305,35 +1117,29 @@ 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_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_EQ(key_calls.size(), 2); 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(); } @@ -1359,10 +1165,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( @@ -1374,10 +1176,6 @@ TEST(KeyboardTest, DeadKeyThatCombines) { kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Press E tester.InjectMessages( 2, @@ -1392,11 +1190,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( @@ -1406,9 +1199,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 @@ -1433,10 +1223,6 @@ TEST(KeyboardTest, DeadKeyWithoutDeadMaskThatCombines) { kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Press 6^ tester.InjectMessages( 2, @@ -1450,10 +1236,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)); @@ -1463,10 +1245,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( @@ -1478,10 +1256,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, @@ -1496,11 +1270,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( @@ -1510,9 +1279,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 @@ -1537,10 +1303,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( @@ -1552,10 +1314,6 @@ TEST(KeyboardTest, DeadKeyThatDoesNotCombine) { kNotSynthesized); clear_key_calls(); - tester.InjectPendingEvents(); - EXPECT_EQ(key_calls.size(), 0); - clear_key_calls(); - // Press 1 tester.InjectMessages( 3, @@ -1573,10 +1331,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( @@ -1586,9 +1340,85 @@ 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 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(), 1); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeDown, + kPhysicalBackquote, kLogicalBackquote, "`", + kNotSynthesized); + clear_key_calls(); + // Key down event responded with false. + recorded_callbacks.front()(false); + 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); + + // 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. @@ -1617,20 +1447,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( @@ -1640,10 +1456,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 @@ -1663,10 +1475,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 @@ -1731,17 +1539,15 @@ 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"); + EXPECT_EQ(key_calls.size(), 0); clear_key_calls(); // Resolve the first event. recorded_callbacks.front()(false); - EXPECT_EQ(tester.InjectPendingEvents('a'), 2); - 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(); } @@ -1787,7 +1593,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(); @@ -1795,12 +1600,90 @@ 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(); } +// 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); diff --git a/shell/platform/windows/testing/wm_builders.cc b/shell/platform/windows/testing/wm_builders.cc index 0d0aea777ca72..396aca2ddd069 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, diff --git a/shell/platform/windows/testing/wm_builders.h b/shell/platform/windows/testing/wm_builders.h index 747d7075f6393..fd3b8914388db 100644 --- a/shell/platform/windows/testing/wm_builders.h +++ b/shell/platform/windows/testing/wm_builders.h @@ -111,6 +111,11 @@ typedef struct WmCharInfo { uint16_t repeat_count = 1; + // The 25th bit of the LParam. + // + // Some messages are sent with bit25 set. Its meaning is yet unknown. + bool bit25 = 0; + Win32Message Build(LRESULT expected_result = kWmResultDontCheck, HWND hWnd = NULL); } WmCharInfo; diff --git a/shell/platform/windows/window_win32.cc b/shell/platform/windows/window_win32.cc index 965ef01bef73a..aad1c1e17bb28 100644 --- a/shell/platform/windows/window_win32.cc +++ b/shell/platform/windows/window_win32.cc @@ -551,10 +551,8 @@ 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); } } // namespace flutter diff --git a/shell/platform/windows/window_win32.h b/shell/platform/windows/window_win32.h index e5d292fcb7556..4c30275fcf37d 100644 --- a/shell/platform/windows/window_win32.h +++ b/shell/platform/windows/window_win32.h @@ -49,9 +49,9 @@ class WindowWin32 : public KeyboardManagerWin32::WindowDelegate { virtual uint32_t Win32MapVkToChar(uint32_t virtual_key) override; // |KeyboardManagerWin32::WindowDelegate| - virtual UINT Win32DispatchEvent(UINT cInputs, - LPINPUT pInputs, - int cbSize) override; + virtual UINT Win32DispatchMessage(UINT Msg, + WPARAM wParam, + LPARAM lParam) override; protected: // Converts a c string to a wide unicode string.