Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
keyboard_unittests.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
20
21#include "gmock/gmock.h"
22#include "gtest/gtest.h"
23#include "rapidjson/stringbuffer.h"
24#include "rapidjson/writer.h"
25
26#include <functional>
27#include <list>
28#include <vector>
29
30using testing::_;
31using testing::Invoke;
32using testing::Return;
33using namespace ::flutter::testing::keycodes;
34
35namespace flutter {
36namespace testing {
37
38namespace {
39
40constexpr SHORT kStateMaskToggled = 0x01;
41constexpr SHORT kStateMaskPressed = 0x80;
42
43constexpr uint64_t kScanCodeBackquote = 0x29;
44constexpr uint64_t kScanCodeKeyA = 0x1e;
45constexpr uint64_t kScanCodeKeyB = 0x30;
46constexpr uint64_t kScanCodeKeyE = 0x12;
47constexpr uint64_t kScanCodeKeyF = 0x21;
48constexpr uint64_t kScanCodeKeyO = 0x18;
49constexpr uint64_t kScanCodeKeyQ = 0x10;
50constexpr uint64_t kScanCodeKeyW = 0x11;
51constexpr uint64_t kScanCodeDigit1 = 0x02;
52constexpr uint64_t kScanCodeDigit2 = 0x03;
53constexpr uint64_t kScanCodeDigit6 = 0x07;
54// constexpr uint64_t kScanCodeNumpad1 = 0x4f;
55// constexpr uint64_t kScanCodeNumLock = 0x45;
56constexpr uint64_t kScanCodeControl = 0x1d;
57constexpr uint64_t kScanCodeMetaLeft = 0x5b;
58constexpr uint64_t kScanCodeMetaRight = 0x5c;
59constexpr uint64_t kScanCodeAlt = 0x38;
60constexpr uint64_t kScanCodeShiftLeft = 0x2a;
61constexpr uint64_t kScanCodeShiftRight = 0x36;
62constexpr uint64_t kScanCodeBracketLeft = 0x1a;
63constexpr uint64_t kScanCodeArrowLeft = 0x4b;
64constexpr uint64_t kScanCodeEnter = 0x1c;
65constexpr uint64_t kScanCodeBackspace = 0x0e;
66
67constexpr uint64_t kVirtualDigit1 = 0x31;
68constexpr uint64_t kVirtualKeyA = 0x41;
69constexpr uint64_t kVirtualKeyB = 0x42;
70constexpr uint64_t kVirtualKeyE = 0x45;
71constexpr uint64_t kVirtualKeyF = 0x46;
72constexpr uint64_t kVirtualKeyO = 0x4f;
73constexpr uint64_t kVirtualKeyQ = 0x51;
74constexpr uint64_t kVirtualKeyW = 0x57;
75
76constexpr bool kSynthesized = true;
77constexpr bool kNotSynthesized = false;
78
79typedef UINT (*MapVirtualKeyLayout)(UINT uCode, UINT uMapType);
80typedef std::function<UINT(UINT)> MapVirtualKeyToChar;
81
82UINT LayoutDefault(UINT uCode, UINT uMapType) {
83 return MapVirtualKey(uCode, uMapType);
84}
85
86UINT LayoutFrench(UINT uCode, UINT uMapType) {
87 switch (uMapType) {
88 case MAPVK_VK_TO_CHAR:
89 switch (uCode) {
90 case 0xDD:
91 return 0x8000005E;
92 default:
93 return MapVirtualKey(uCode, MAPVK_VK_TO_CHAR);
94 }
95 default:
96 return MapVirtualKey(uCode, uMapType);
97 }
98}
99
100class TestKeyboardManager : public KeyboardManager {
101 public:
102 explicit TestKeyboardManager(WindowDelegate* delegate)
103 : KeyboardManager(delegate) {}
104
105 bool DuringRedispatch() { return during_redispatch_; }
106
107 protected:
108 void RedispatchEvent(std::unique_ptr<PendingEvent> event) override {
109 FML_DCHECK(!during_redispatch_)
110 << "RedispatchEvent called while already redispatching an event";
111 during_redispatch_ = true;
112 KeyboardManager::RedispatchEvent(std::move(event));
113 during_redispatch_ = false;
114 }
115
116 private:
117 bool during_redispatch_ = false;
118
119 FML_DISALLOW_COPY_AND_ASSIGN(TestKeyboardManager);
120};
121
122// Injecting this kind of keyboard change means that a key state (the true
123// state for a key, typically a modifier) should be changed.
124struct KeyStateChange {
125 uint32_t key;
128};
129
130// Injecting this kind of keyboard change does not make any changes to the
131// keyboard system, but indicates that a forged event is expected here, and
132// that `KeyStateChange`s after this will be applied only after the forged
133// event.
134//
135// See `IsKeyDownAltRight` for explaination for foged events.
136struct ExpectForgedMessage {
137 explicit ExpectForgedMessage(Win32Message message) : message(message) {};
138
139 Win32Message message;
140};
141
142struct KeyboardChange {
143 // The constructors are intentionally for implicit conversion.
144
145 KeyboardChange(Win32Message message) : type(kMessage) {
146 content.message = message;
147 }
148
149 KeyboardChange(KeyStateChange change) : type(kKeyStateChange) {
150 content.key_state_change = change;
151 }
152
153 KeyboardChange(ExpectForgedMessage forged_message)
154 : type(kExpectForgedMessage) {
155 content.expected_forged_message = forged_message.message;
156 }
157
158 enum Type {
159 kMessage,
160 kKeyStateChange,
161 kExpectForgedMessage,
163
164 union {
165 Win32Message message;
166 KeyStateChange key_state_change;
169};
170
171class TestKeystate {
172 public:
173 void Set(uint32_t virtual_key, bool pressed, bool toggled_on = false) {
174 state_[virtual_key] = (pressed ? kStateMaskPressed : 0) |
175 (toggled_on ? kStateMaskToggled : 0);
176 }
177
178 SHORT Get(uint32_t virtual_key) { return state_[virtual_key]; }
179
180 private:
181 std::map<uint32_t, SHORT> state_;
182};
183
184class MockKeyboardManagerDelegate : public KeyboardManager::WindowDelegate,
185 protected MockMessageQueue {
186 public:
187 MockKeyboardManagerDelegate(WindowBindingHandlerDelegate* view,
188 MapVirtualKeyToChar map_vk_to_char)
189 : view_(view), map_vk_to_char_(std::move(map_vk_to_char)) {
190 keyboard_manager_ = std::make_unique<TestKeyboardManager>(this);
191 }
192 virtual ~MockKeyboardManagerDelegate() {}
193
194 // |KeyboardManager::WindowDelegate|
195 void OnKey(int key,
196 int scancode,
197 int action,
198 char32_t character,
199 bool extended,
200 bool was_down,
201 KeyEventCallback callback) override {
202 view_->OnKey(key, scancode, action, character, extended, was_down,
203 callback);
204 }
205
206 // |KeyboardManager::WindowDelegate|
207 void OnText(const std::u16string& text) override { view_->OnText(text); }
208
209 SHORT GetKeyState(int virtual_key) { return key_state_.Get(virtual_key); }
210
211 void InjectKeyboardChanges(std::vector<KeyboardChange> changes) {
212 // First queue all messages to enable peeking.
213 for (const KeyboardChange& change : changes) {
214 switch (change.type) {
215 case KeyboardChange::kMessage:
216 PushBack(&change.content.message);
217 break;
218 default:
219 break;
220 }
221 }
222 for (const KeyboardChange& change : changes) {
223 switch (change.type) {
224 case KeyboardChange::kMessage:
225 DispatchFront();
226 break;
227 case KeyboardChange::kExpectForgedMessage:
228 forged_message_expectations_.push_back(ForgedMessageExpectation{
229 .message = change.content.expected_forged_message,
230 });
231 break;
232 case KeyboardChange::kKeyStateChange: {
233 const KeyStateChange& state_change = change.content.key_state_change;
234 if (forged_message_expectations_.empty()) {
235 key_state_.Set(state_change.key, state_change.pressed,
236 state_change.toggled_on);
237 } else {
238 forged_message_expectations_.back()
239 .state_changes_afterwards.push_back(state_change);
240 }
241 break;
242 }
243 default:
244 FML_LOG(FATAL) << "Unhandled KeyboardChange type " << change.type;
245 }
246 }
247 }
248
249 std::list<Win32Message>& RedispatchedMessages() {
250 return redispatched_messages_;
251 }
252
253 protected:
254 BOOL Win32PeekMessage(LPMSG lpMsg,
255 UINT wMsgFilterMin,
256 UINT wMsgFilterMax,
257 UINT wRemoveMsg) override {
258 return MockMessageQueue::Win32PeekMessage(lpMsg, wMsgFilterMin,
259 wMsgFilterMax, wRemoveMsg);
260 }
261
262 uint32_t Win32MapVkToChar(uint32_t virtual_key) override {
263 return map_vk_to_char_(virtual_key);
264 }
265
266 // This method is called for each message injected by test cases with
267 // `tester.InjectMessages`.
268 LRESULT Win32SendMessage(UINT const message,
269 WPARAM const wparam,
270 LPARAM const lparam) override {
271 return keyboard_manager_->HandleMessage(message, wparam, lparam)
272 ? 0
274 }
275
276 // This method is called when the keyboard manager redispatches messages
277 // or forges messages (such as CtrlLeft up right before AltGr up).
278 UINT Win32DispatchMessage(UINT Msg, WPARAM wParam, LPARAM lParam) override {
279 bool handled = keyboard_manager_->HandleMessage(Msg, wParam, lParam);
280 if (keyboard_manager_->DuringRedispatch()) {
281 redispatched_messages_.push_back(Win32Message{
282 .message = Msg,
283 .wParam = wParam,
284 .lParam = lParam,
285 });
286 EXPECT_FALSE(handled);
287 } else {
288 EXPECT_FALSE(forged_message_expectations_.empty());
289 ForgedMessageExpectation expectation =
290 forged_message_expectations_.front();
291 forged_message_expectations_.pop_front();
292 EXPECT_EQ(expectation.message.message, Msg);
293 EXPECT_EQ(expectation.message.wParam, wParam);
294 EXPECT_EQ(expectation.message.lParam, lParam);
295 if (expectation.message.expected_result != kWmResultDontCheck) {
296 EXPECT_EQ(expectation.message.expected_result,
297 handled ? kWmResultZero : kWmResultDefault);
298 }
299 for (const KeyStateChange& change :
300 expectation.state_changes_afterwards) {
301 key_state_.Set(change.key, change.pressed, change.toggled_on);
302 }
303 }
304 return 0;
305 }
306
307 private:
308 struct ForgedMessageExpectation {
309 Win32Message message;
310 std::list<KeyStateChange> state_changes_afterwards;
311 };
312
313 WindowBindingHandlerDelegate* view_;
314 std::unique_ptr<TestKeyboardManager> keyboard_manager_;
315 std::list<ForgedMessageExpectation> forged_message_expectations_;
316 MapVirtualKeyToChar map_vk_to_char_;
317 TestKeystate key_state_;
318 std::list<Win32Message> redispatched_messages_;
319
320 FML_DISALLOW_COPY_AND_ASSIGN(MockKeyboardManagerDelegate);
321};
322
323typedef struct {
324 enum {
325 kKeyCallOnKey,
326 kKeyCallOnText,
327 kKeyCallTextMethodCall,
328 } type;
329
330 // Only one of the following fields should be assigned.
331 FlutterKeyEvent key_event; // For kKeyCallOnKey
332 std::u16string text; // For kKeyCallOnText
333 std::string text_method_call; // For kKeyCallTextMethodCall
334} KeyCall;
335
336// A FlutterWindowsView that spies on text.
337class TestFlutterWindowsView : public FlutterWindowsView {
338 public:
339 TestFlutterWindowsView(FlutterWindowsEngine* engine,
340 std::unique_ptr<WindowBindingHandler> window,
341 std::function<void(KeyCall)> on_key_call)
342 : on_key_call_(on_key_call),
343 FlutterWindowsView(kImplicitViewId,
344 engine,
345 std::move(window),
346 false,
347 BoxConstraints()) {}
348
349 void OnText(const std::u16string& text) override {
350 on_key_call_(KeyCall{
351 .type = KeyCall::kKeyCallOnText,
352 .text = text,
353 });
354 }
355
356 private:
357 std::function<void(KeyCall)> on_key_call_;
358
359 FML_DISALLOW_COPY_AND_ASSIGN(TestFlutterWindowsView);
360};
361
362class KeyboardTester {
363 public:
364 using ResponseHandler =
366
367 explicit KeyboardTester(WindowsTestContext& context)
368 : callback_handler_(RespondValue(false)),
369 map_virtual_key_layout_(LayoutDefault) {
370 engine_ = GetTestEngine(context);
371 view_ = std::make_unique<TestFlutterWindowsView>(
372 engine_.get(),
373 // The WindowBindingHandler is used for window size and such, and
374 // doesn't affect keyboard.
375 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>(),
376 [this](KeyCall key_call) { key_calls.push_back(key_call); });
377
378 EngineModifier modifier{engine_.get()};
379 modifier.SetImplicitView(view_.get());
380 modifier.InitializeKeyboard();
381
382 window_ = std::make_unique<MockKeyboardManagerDelegate>(
383 view_.get(), [this](UINT virtual_key) -> SHORT {
384 return map_virtual_key_layout_(virtual_key, MAPVK_VK_TO_CHAR);
385 });
386 }
387
388 TestFlutterWindowsView& GetView() { return *view_; }
389 MockKeyboardManagerDelegate& GetWindow() { return *window_; }
390
391 // Reset the keyboard by invoking the engine restart handler.
392 void ResetKeyboard() { EngineModifier{engine_.get()}.Restart(); }
393
394 // Set all events to be handled (true) or unhandled (false).
395 void Responding(bool response) { callback_handler_ = RespondValue(response); }
396
397 // Manually handle event callback of the onKeyData embedder API.
398 //
399 // On every onKeyData call, the |handler| will be invoked with the target
400 // key data and the result callback. Immediately calling the callback with
401 // a boolean is equivalent to setting |Responding| with the boolean. However,
402 // |LateResponding| allows storing the callback to call later.
403 void LateResponding(
405 callback_handler_ = std::move(handler);
406 }
407
408 void SetLayout(MapVirtualKeyLayout layout) {
409 map_virtual_key_layout_ = layout == nullptr ? LayoutDefault : layout;
410 }
411
412 void InjectKeyboardChanges(std::vector<KeyboardChange> changes) {
413 FML_DCHECK(window_ != nullptr);
414 window_->InjectKeyboardChanges(std::move(changes));
415 }
416
417 // Simulates receiving a platform message from the framework.
418 void InjectPlatformMessage(const char* channel,
419 const char* method,
420 const char* args) {
421 rapidjson::Document args_doc;
422 args_doc.Parse(args);
423 FML_DCHECK(!args_doc.HasParseError());
424
425 rapidjson::Document message_doc(rapidjson::kObjectType);
426 auto& allocator = message_doc.GetAllocator();
427 message_doc.AddMember("method", rapidjson::Value(method, allocator),
428 allocator);
429 message_doc.AddMember("args", args_doc, allocator);
430
431 rapidjson::StringBuffer buffer;
432 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
433 message_doc.Accept(writer);
434
435 std::unique_ptr<std::vector<uint8_t>> data =
439 sizeof(FlutterPlatformMessage), // struct_size
440 channel, // channel
441 data->data(), // message
442 data->size(), // message_size
443 &response_handle, // response_handle
444 };
445 view_->GetEngine()->HandlePlatformMessage(&message);
446 }
447
448 // Get the number of redispatched messages since the last clear, then clear
449 // the counter.
450 size_t RedispatchedMessageCountAndClear() {
451 auto& messages = window_->RedispatchedMessages();
452 size_t count = messages.size();
453 messages.clear();
454 return count;
455 }
456
457 void clear_key_calls() {
458 for (KeyCall& key_call : key_calls) {
459 if (key_call.type == KeyCall::kKeyCallOnKey &&
460 key_call.key_event.character != nullptr) {
461 delete[] key_call.key_event.character;
462 }
463 }
464 key_calls.clear();
465 }
466
467 std::vector<KeyCall> key_calls;
468
469 private:
470 std::unique_ptr<FlutterWindowsEngine> engine_;
471 std::unique_ptr<TestFlutterWindowsView> view_;
472 std::unique_ptr<MockKeyboardManagerDelegate> window_;
474 MapVirtualKeyLayout map_virtual_key_layout_;
475
476 // Returns an engine instance configured with dummy project path values, and
477 // overridden methods for sending platform messages, so that the engine can
478 // respond as if the framework were connected.
479 std::unique_ptr<FlutterWindowsEngine> GetTestEngine(
480 WindowsTestContext& context) {
481 FlutterWindowsEngineBuilder builder{context};
482
483 builder.SetCreateKeyboardHandlerCallbacks(
484 [this](int virtual_key) -> SHORT {
485 // `window_` is not initialized yet when this callback is first
486 // called.
487 return window_ ? window_->GetKeyState(virtual_key) : 0;
488 },
489 [this](UINT virtual_key, bool extended) -> SHORT {
490 return map_virtual_key_layout_(
491 virtual_key, extended ? MAPVK_VK_TO_VSC_EX : MAPVK_VK_TO_VSC);
492 });
493
494 auto engine = builder.Build();
495
496 EngineModifier modifier(engine.get());
497
498 auto key_response_controller =
499 std::make_shared<MockKeyResponseController>();
500 key_response_controller->SetEmbedderResponse(
501 [&key_calls = key_calls, &callback_handler = callback_handler_](
502 const FlutterKeyEvent* event,
504 FlutterKeyEvent clone_event = *event;
505 clone_event.character = event->character == nullptr
506 ? nullptr
507 : clone_string(event->character);
508 key_calls.push_back(KeyCall{
509 .type = KeyCall::kKeyCallOnKey,
510 .key_event = clone_event,
511 });
513 });
514 key_response_controller->SetTextInputResponse(
515 [&key_calls =
516 key_calls](std::unique_ptr<rapidjson::Document> document) {
517 rapidjson::StringBuffer buffer;
518 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
519 document->Accept(writer);
520 key_calls.push_back(KeyCall{
521 .type = KeyCall::kKeyCallTextMethodCall,
522 .text_method_call = buffer.GetString(),
523 });
524 });
525 MockEmbedderApiForKeyboard(modifier, key_response_controller);
526
527 engine->Run();
528
529 return engine;
530 }
531
533 bool value) {
534 return [value](const FlutterKeyEvent* event,
536 callback(value);
537 };
538 }
539
540 private:
542};
543
544class KeyboardTest : public WindowsTest {
545 public:
546 KeyboardTest() = default;
547 virtual ~KeyboardTest() = default;
548
549 private:
550 FML_DISALLOW_COPY_AND_ASSIGN(KeyboardTest);
551};
552
553} // namespace
554
555// Define compound `expect` in macros. If they're defined in functions, the
556// stacktrace wouldn't print where the function is called in the unit tests.
557
558#define EXPECT_CALL_IS_EVENT(_key_call, _type, _physical, _logical, \
559 _character, _synthesized) \
560 EXPECT_EQ(_key_call.type, KeyCall::kKeyCallOnKey); \
561 EXPECT_EVENT_EQUALS(_key_call.key_event, _type, _physical, _logical, \
562 _character, _synthesized);
563
564#define EXPECT_CALL_IS_TEXT(_key_call, u16_string) \
565 EXPECT_EQ(_key_call.type, KeyCall::kKeyCallOnText); \
566 EXPECT_EQ(_key_call.text, u16_string);
567
568#define EXPECT_CALL_IS_TEXT_METHOD_CALL(_key_call, json_string) \
569 EXPECT_EQ(_key_call.type, KeyCall::kKeyCallTextMethodCall); \
570 EXPECT_STREQ(_key_call.text_method_call.c_str(), json_string);
571
572TEST_F(KeyboardTest, LowerCaseAHandled) {
573 KeyboardTester tester{GetContext()};
574 tester.Responding(true);
575
576 // US Keyboard layout
577
578 // Press A
579 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
583 kWmResultZero)});
584
585 EXPECT_EQ(tester.key_calls.size(), 1);
587 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
588 tester.clear_key_calls();
589 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
590
591 // Release A
592 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
594 kWmResultZero)});
595
596 EXPECT_EQ(tester.key_calls.size(), 1);
597 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
598 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
599 tester.clear_key_calls();
600 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
601}
602
603TEST_F(KeyboardTest, LowerCaseAUnhandled) {
604 KeyboardTester tester{GetContext()};
605 tester.Responding(false);
606
607 // US Keyboard layout
608
609 // Press A
610 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
614 kWmResultZero)});
615
616 EXPECT_EQ(tester.key_calls.size(), 2);
618 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
619 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"a");
620 tester.clear_key_calls();
621 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
622
623 // Release A
624 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
626 kWmResultZero)});
627
628 EXPECT_EQ(tester.key_calls.size(), 1);
629 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
630 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
631 tester.clear_key_calls();
632 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
633}
634
635TEST_F(KeyboardTest, ArrowLeftHandled) {
636 KeyboardTester tester{GetContext()};
637 tester.Responding(true);
638
639 // US Keyboard layout
640
641 // Press ArrowLeft
642 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
643 WmKeyDownInfo{VK_LEFT, kScanCodeArrowLeft, kExtended, kWasUp}.Build(
644 kWmResultZero)});
645
646 EXPECT_EQ(tester.key_calls.size(), 1);
648 kPhysicalArrowLeft, kLogicalArrowLeft, "",
649 kNotSynthesized);
650 tester.clear_key_calls();
651 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
652
653 // Release ArrowLeft
654 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
655 WmKeyUpInfo{VK_LEFT, kScanCodeArrowLeft, kExtended}.Build(
656 kWmResultZero)});
657
658 EXPECT_EQ(tester.key_calls.size(), 1);
659 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
660 kPhysicalArrowLeft, kLogicalArrowLeft, "",
661 kNotSynthesized);
662 tester.clear_key_calls();
663 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
664}
665
666TEST_F(KeyboardTest, ArrowLeftUnhandled) {
667 KeyboardTester tester{GetContext()};
668 tester.Responding(false);
669
670 // US Keyboard layout
671
672 // Press ArrowLeft
673 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
674 WmKeyDownInfo{VK_LEFT, kScanCodeArrowLeft, kExtended, kWasUp}.Build(
675 kWmResultZero)});
676
677 EXPECT_EQ(tester.key_calls.size(), 1);
679 kPhysicalArrowLeft, kLogicalArrowLeft, "",
680 kNotSynthesized);
681 tester.clear_key_calls();
682 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
683
684 // Release ArrowLeft
685 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
686 WmKeyUpInfo{VK_LEFT, kScanCodeArrowLeft, kExtended}.Build(
687 kWmResultZero)});
688
689 EXPECT_EQ(tester.key_calls.size(), 1);
690 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
691 kPhysicalArrowLeft, kLogicalArrowLeft, "",
692 kNotSynthesized);
693 tester.clear_key_calls();
694 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
695}
696
697TEST_F(KeyboardTest, ShiftLeftUnhandled) {
698 KeyboardTester tester{GetContext()};
699 tester.Responding(false);
700
701 // US Keyboard layout
702
703 // Press ShiftLeft
704 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
705 KeyStateChange{VK_LSHIFT, true, false},
707 kWmResultZero)});
708
709 EXPECT_EQ(tester.key_calls.size(), 1);
711 kPhysicalShiftLeft, kLogicalShiftLeft, "",
712 kNotSynthesized);
713 tester.clear_key_calls();
714 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
715
716 // Hold ShiftLeft
717 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
719 kWmResultZero)});
720
721 EXPECT_EQ(tester.key_calls.size(), 1);
723 kPhysicalShiftLeft, kLogicalShiftLeft, "",
724 kNotSynthesized);
725 tester.clear_key_calls();
726 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
727
728 // Release ShiftLeft
729 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
730 KeyStateChange{VK_LSHIFT, false, true},
732 kWmResultZero)});
733
734 EXPECT_EQ(tester.key_calls.size(), 1);
735 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
736 kPhysicalShiftLeft, kLogicalShiftLeft, "",
737 kNotSynthesized);
738 tester.clear_key_calls();
739 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
740}
741
742TEST_F(KeyboardTest, ShiftRightUnhandled) {
743 KeyboardTester tester{GetContext()};
744 tester.Responding(false);
745
746 // US Keyboard layout
747
748 // Press ShiftRight
749 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
750 KeyStateChange{VK_RSHIFT, true, false},
752 kWmResultZero)});
753
754 EXPECT_EQ(tester.key_calls.size(), 1);
756 kPhysicalShiftRight, kLogicalShiftRight, "",
757 kNotSynthesized);
758 tester.clear_key_calls();
759 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
760
761 // Release ShiftRight
762 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
763 KeyStateChange{VK_RSHIFT, false, true},
765 kWmResultZero)});
766
767 EXPECT_EQ(tester.key_calls.size(), 1);
768 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
769 kPhysicalShiftRight, kLogicalShiftRight, "",
770 kNotSynthesized);
771 tester.clear_key_calls();
772 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
773}
774
775TEST_F(KeyboardTest, CtrlLeftUnhandled) {
776 KeyboardTester tester{GetContext()};
777 tester.Responding(false);
778
779 // US Keyboard layout
780
781 // Press CtrlLeft
782 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
783 KeyStateChange{VK_LCONTROL, true, false},
784 WmKeyDownInfo{VK_CONTROL, kScanCodeControl, kNotExtended, kWasUp}.Build(
785 kWmResultZero)});
786
787 EXPECT_EQ(tester.key_calls.size(), 1);
789 kPhysicalControlLeft, kLogicalControlLeft, "",
790 kNotSynthesized);
791 tester.clear_key_calls();
792 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
793
794 // Release CtrlLeft
795 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
796 KeyStateChange{VK_LCONTROL, false, true},
797 WmKeyUpInfo{VK_SHIFT, kScanCodeControl, kNotExtended}.Build(
798 kWmResultZero)});
799
800 EXPECT_EQ(tester.key_calls.size(), 1);
801 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
802 kPhysicalControlLeft, kLogicalControlLeft, "",
803 kNotSynthesized);
804 tester.clear_key_calls();
805 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
806}
807
808TEST_F(KeyboardTest, CtrlRightUnhandled) {
809 KeyboardTester tester{GetContext()};
810 tester.Responding(false);
811
812 // US Keyboard layout
813
814 // Press CtrlRight
815 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
816 KeyStateChange{VK_RCONTROL, true, false},
817 WmKeyDownInfo{VK_CONTROL, kScanCodeControl, kExtended, kWasUp}.Build(
818 kWmResultZero)});
819
820 EXPECT_EQ(tester.key_calls.size(), 1);
822 kPhysicalControlRight, kLogicalControlRight, "",
823 kNotSynthesized);
824 tester.clear_key_calls();
825 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
826
827 // Release CtrlRight
828 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
829 KeyStateChange{VK_RCONTROL, false, true},
830 WmKeyUpInfo{VK_CONTROL, kScanCodeControl, kExtended}.Build(
831 kWmResultZero)});
832
833 EXPECT_EQ(tester.key_calls.size(), 1);
834 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
835 kPhysicalControlRight, kLogicalControlRight, "",
836 kNotSynthesized);
837 tester.clear_key_calls();
838 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
839}
840
841TEST_F(KeyboardTest, AltLeftUnhandled) {
842 KeyboardTester tester{GetContext()};
843 tester.Responding(false);
844
845 // US Keyboard layout
846
847 // Press AltLeft. AltLeft is a SysKeyDown event.
848 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
849 KeyStateChange{VK_LMENU, true, false},
850 WmSysKeyDownInfo{VK_MENU, kScanCodeAlt, kNotExtended, kWasUp}.Build(
851 kWmResultDefault)}); // Always pass to the default WndProc.
852
853 EXPECT_EQ(tester.key_calls.size(), 1);
855 kPhysicalAltLeft, kLogicalAltLeft, "", kNotSynthesized);
856 tester.clear_key_calls();
857 // Don't redispatch sys messages.
858 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
859
860 // Release AltLeft. AltLeft is a SysKeyUp event.
861 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
862 KeyStateChange{VK_LMENU, false, true},
863 WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kNotExtended}.Build(
864 kWmResultDefault)}); // Always pass to the default WndProc.
865
866 EXPECT_EQ(tester.key_calls.size(), 1);
867 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
868 kPhysicalAltLeft, kLogicalAltLeft, "", kNotSynthesized);
869 tester.clear_key_calls();
870 // Don't redispatch sys messages.
871 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
872}
873
874TEST_F(KeyboardTest, AltRightUnhandled) {
875 KeyboardTester tester{GetContext()};
876 tester.Responding(false);
877
878 // US Keyboard layout
879
880 // Press AltRight. AltRight is a SysKeyDown event.
881 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
882 KeyStateChange{VK_RMENU, true, false},
883 WmSysKeyDownInfo{VK_MENU, kScanCodeAlt, kExtended, kWasUp}.Build(
884 kWmResultDefault)}); // Always pass to the default WndProc.
885
886 EXPECT_EQ(tester.key_calls.size(), 1);
888 kPhysicalAltRight, kLogicalAltRight, "",
889 kNotSynthesized);
890 tester.clear_key_calls();
891 // Don't redispatch sys messages.
892 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
893
894 // Release AltRight. AltRight is a SysKeyUp event.
895 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
896 KeyStateChange{VK_RMENU, false, true},
897 WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kExtended}.Build(
898 kWmResultDefault)}); // Always pass to the default WndProc.
899
900 EXPECT_EQ(tester.key_calls.size(), 1);
901 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
902 kPhysicalAltRight, kLogicalAltRight, "",
903 kNotSynthesized);
904 tester.clear_key_calls();
905 // Don't redispatch sys messages.
906 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
907}
908
909TEST_F(KeyboardTest, MetaLeftUnhandled) {
910 KeyboardTester tester{GetContext()};
911 tester.Responding(false);
912
913 // US Keyboard layout
914
915 // Press MetaLeft
916 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
917 KeyStateChange{VK_LWIN, true, false},
918 WmKeyDownInfo{VK_LWIN, kScanCodeMetaLeft, kExtended, kWasUp}.Build(
919 kWmResultZero)});
920
921 EXPECT_EQ(tester.key_calls.size(), 1);
923 kPhysicalMetaLeft, kLogicalMetaLeft, "",
924 kNotSynthesized);
925 tester.clear_key_calls();
926 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
927
928 // Release MetaLeft
929 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
930 KeyStateChange{VK_LWIN, false, true},
931 WmKeyUpInfo{VK_LWIN, kScanCodeMetaLeft, kExtended}.Build(kWmResultZero)});
932
933 EXPECT_EQ(tester.key_calls.size(), 1);
934 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
935 kPhysicalMetaLeft, kLogicalMetaLeft, "",
936 kNotSynthesized);
937 tester.clear_key_calls();
938 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
939}
940
941TEST_F(KeyboardTest, MetaRightUnhandled) {
942 KeyboardTester tester{GetContext()};
943 tester.Responding(false);
944
945 // US Keyboard layout
946
947 // Press MetaRight
948 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
949 KeyStateChange{VK_RWIN, true, false},
950 WmKeyDownInfo{VK_RWIN, kScanCodeMetaRight, kExtended, kWasUp}.Build(
951 kWmResultZero)});
952
953 EXPECT_EQ(tester.key_calls.size(), 1);
955 kPhysicalMetaRight, kLogicalMetaRight, "",
956 kNotSynthesized);
957 tester.clear_key_calls();
958 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
959
960 // Release MetaRight
961 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
962 KeyStateChange{VK_RWIN, false, true},
963 WmKeyUpInfo{VK_RWIN, kScanCodeMetaRight, kExtended}.Build(
964 kWmResultZero)});
965
966 EXPECT_EQ(tester.key_calls.size(), 1);
967 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
968 kPhysicalMetaRight, kLogicalMetaRight, "",
969 kNotSynthesized);
970 tester.clear_key_calls();
971 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
972}
973
974// Press and hold A. This should generate a repeat event.
975TEST_F(KeyboardTest, RepeatA) {
976 KeyboardTester tester{GetContext()};
977 tester.Responding(true);
978
979 // Press A
980 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
984 kWmResultZero)});
985
986 // Hold A
987 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
991 kWmResultZero)});
992
993 EXPECT_EQ(tester.key_calls.size(), 2);
995 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
997 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
998 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
999}
1000
1001// Press A, hot restart the engine, and hold A.
1002// This should reset the keyboard's state and generate
1003// two separate key down events.
1004TEST_F(KeyboardTest, RestartClearsKeyboardState) {
1005 KeyboardTester tester{GetContext()};
1006 tester.Responding(true);
1007
1008 // Press A
1009 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1013 kWmResultZero)});
1014
1015 // Reset the keyboard's state.
1016 tester.ResetKeyboard();
1017
1018 // Hold A. Notice the message declares the key is already down, however, the
1019 // the keyboard does not send a repeat event as its state was reset.
1020 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1024 kWmResultZero)});
1025
1026 EXPECT_EQ(tester.key_calls.size(), 2);
1028 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
1030 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
1031 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
1032}
1033
1034// Press Enter, lose focus before the up event arrives, and press Enter again
1035// once focus returns. The second down event should not be dropped.
1036TEST_F(KeyboardTest, FreshKeyDownAfterMissedUpIsDelivered) {
1037 KeyboardTester tester{GetContext()};
1038 tester.Responding(true);
1039
1040 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1041 WmKeyDownInfo{VK_RETURN, kScanCodeEnter, kNotExtended, kWasUp}.Build(
1042 kWmResultZero)});
1043
1044 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1045 WmKeyDownInfo{VK_RETURN, kScanCodeEnter, kNotExtended, kWasUp}.Build(
1046 kWmResultZero)});
1047
1048 ASSERT_EQ(tester.key_calls.size(), 3u);
1050 kPhysicalEnter, kLogicalEnter, "", kNotSynthesized);
1051 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
1052 kPhysicalEnter, kLogicalEnter, "", kSynthesized);
1054 kPhysicalEnter, kLogicalEnter, "", kNotSynthesized);
1055 tester.clear_key_calls();
1056
1057 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1058 WmKeyUpInfo{VK_RETURN, kScanCodeEnter, kNotExtended}.Build(
1059 kWmResultZero)});
1060
1061 ASSERT_EQ(tester.key_calls.size(), 1u);
1062 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1063 kPhysicalEnter, kLogicalEnter, "", kNotSynthesized);
1064 tester.clear_key_calls();
1065 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0u);
1066}
1067
1068// Press Shift-A. This is special because Win32 gives 'A' as character for the
1069// KeyA press.
1070TEST_F(KeyboardTest, ShiftLeftKeyA) {
1071 KeyboardTester tester{GetContext()};
1072 tester.Responding(false);
1073
1074 // US Keyboard layout
1075
1076 // Press ShiftLeft
1077 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1078 KeyStateChange{VK_LSHIFT, true, true},
1080 kWmResultZero)});
1081
1082 EXPECT_EQ(tester.key_calls.size(), 1);
1084 kPhysicalShiftLeft, kLogicalShiftLeft, "",
1085 kNotSynthesized);
1086 tester.clear_key_calls();
1087 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1088
1089 // Press A
1090 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1094 kWmResultZero)});
1095
1096 EXPECT_EQ(tester.key_calls.size(), 2);
1098 kPhysicalKeyA, kLogicalKeyA, "A", kNotSynthesized);
1099 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"A");
1100 tester.clear_key_calls();
1101 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1102
1103 // Release ShiftLeft
1104 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1105 KeyStateChange{VK_LSHIFT, false, true},
1107 kWmResultZero)});
1108
1109 EXPECT_EQ(tester.key_calls.size(), 1);
1110 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1111 kPhysicalShiftLeft, kLogicalShiftLeft, "",
1112 kNotSynthesized);
1113 tester.clear_key_calls();
1114 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1115
1116 // Release A
1117 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1119 kWmResultZero)});
1120
1121 EXPECT_EQ(tester.key_calls.size(), 1);
1122 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1123 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
1124 tester.clear_key_calls();
1125 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1126}
1127
1128// Press Ctrl-A. This is special because Win32 gives 0x01 as character for the
1129// KeyA press.
1130TEST_F(KeyboardTest, CtrlLeftKeyA) {
1131 KeyboardTester tester{GetContext()};
1132 tester.Responding(false);
1133
1134 // US Keyboard layout
1135
1136 // Press ControlLeft
1137 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1138 KeyStateChange{VK_LCONTROL, true, true},
1139 WmKeyDownInfo{VK_CONTROL, kScanCodeControl, kNotExtended, kWasUp}.Build(
1140 kWmResultZero)});
1141
1142 EXPECT_EQ(tester.key_calls.size(), 1);
1144 kPhysicalControlLeft, kLogicalControlLeft, "",
1145 kNotSynthesized);
1146 tester.clear_key_calls();
1147 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1148
1149 // Press A
1150 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1154 kWmResultZero)});
1155
1156 EXPECT_EQ(tester.key_calls.size(), 1);
1158 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
1159 tester.clear_key_calls();
1160 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1161
1162 // Release A
1163 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1165 kWmResultZero)});
1166
1167 EXPECT_EQ(tester.key_calls.size(), 1);
1168 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1169 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
1170 tester.clear_key_calls();
1171 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1172
1173 // Release ControlLeft
1174 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1175 KeyStateChange{VK_LCONTROL, false, true},
1176 WmKeyUpInfo{VK_CONTROL, kScanCodeControl, kNotExtended}.Build(
1177 kWmResultZero)});
1178
1179 EXPECT_EQ(tester.key_calls.size(), 1);
1180 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1181 kPhysicalControlLeft, kLogicalControlLeft, "",
1182 kNotSynthesized);
1183 tester.clear_key_calls();
1184 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1185}
1186
1187// Press Ctrl-1. This is special because it yields no WM_CHAR for the 1.
1188TEST_F(KeyboardTest, CtrlLeftDigit1) {
1189 KeyboardTester tester{GetContext()};
1190 tester.Responding(false);
1191
1192 // US Keyboard layout
1193
1194 // Press ControlLeft
1195 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1196 KeyStateChange{VK_LCONTROL, true, true},
1197 WmKeyDownInfo{VK_CONTROL, kScanCodeControl, kNotExtended, kWasUp}.Build(
1198 kWmResultZero)});
1199
1200 EXPECT_EQ(tester.key_calls.size(), 1);
1202 kPhysicalControlLeft, kLogicalControlLeft, "",
1203 kNotSynthesized);
1204 tester.clear_key_calls();
1205 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1206
1207 // Press 1
1208 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1209 WmKeyDownInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended, kWasUp}
1210 .Build(kWmResultZero)});
1211
1212 EXPECT_EQ(tester.key_calls.size(), 1);
1214 kPhysicalDigit1, kLogicalDigit1, "", kNotSynthesized);
1215 tester.clear_key_calls();
1216 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1217
1218 // Release 1
1219 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1220 WmKeyUpInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended}.Build(
1221 kWmResultZero)});
1222
1223 EXPECT_EQ(tester.key_calls.size(), 1);
1224 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1225 kPhysicalDigit1, kLogicalDigit1, "", kNotSynthesized);
1226 tester.clear_key_calls();
1227 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1228
1229 // Release ControlLeft
1230 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1231 KeyStateChange{VK_LCONTROL, false, true},
1232 WmKeyUpInfo{VK_CONTROL, kScanCodeControl, kNotExtended}.Build(
1233 kWmResultZero)});
1234
1235 EXPECT_EQ(tester.key_calls.size(), 1);
1236 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1237 kPhysicalControlLeft, kLogicalControlLeft, "",
1238 kNotSynthesized);
1239 tester.clear_key_calls();
1240 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1241}
1242
1243// Press 1 on a French keyboard. This is special because it yields WM_CHAR
1244// with char_code '&'.
1245TEST_F(KeyboardTest, Digit1OnFrenchLayout) {
1246 KeyboardTester tester{GetContext()};
1247 tester.Responding(false);
1248
1249 tester.SetLayout(LayoutFrench);
1250
1251 // Press 1
1252 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1253 WmKeyDownInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended, kWasUp}
1255 WmCharInfo{'&', kScanCodeDigit1, kNotExtended, kWasUp}.Build(
1256 kWmResultZero)});
1257
1258 EXPECT_EQ(tester.key_calls.size(), 2);
1260 kPhysicalDigit1, kLogicalDigit1, "&", kNotSynthesized);
1261 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"&");
1262 tester.clear_key_calls();
1263 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1264
1265 // Release 1
1266 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1267 WmKeyUpInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended}.Build(
1268 kWmResultZero)});
1269
1270 EXPECT_EQ(tester.key_calls.size(), 1);
1271 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1272 kPhysicalDigit1, kLogicalDigit1, "", kNotSynthesized);
1273 tester.clear_key_calls();
1274 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1275}
1276
1277// This tests AltGr-Q on a German keyboard, which should print '@'.
1278TEST_F(KeyboardTest, AltGrModifiedKey) {
1279 KeyboardTester tester{GetContext()};
1280 tester.Responding(false);
1281
1282 // German Keyboard layout
1283
1284 // Press AltGr, which Win32 precedes with a ContrlLeft down.
1285 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1286 KeyStateChange{VK_LCONTROL, true, true},
1287 WmKeyDownInfo{VK_LCONTROL, kScanCodeControl, kNotExtended, kWasUp}.Build(
1289 KeyStateChange{VK_RMENU, true, true},
1290 WmKeyDownInfo{VK_MENU, kScanCodeAlt, kExtended, kWasUp}.Build(
1291 kWmResultZero)});
1292
1293 EXPECT_EQ(tester.key_calls.size(), 2);
1295 kPhysicalControlLeft, kLogicalControlLeft, "",
1296 kNotSynthesized);
1298 kPhysicalAltRight, kLogicalAltRight, "",
1299 kNotSynthesized);
1300 tester.clear_key_calls();
1301 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1302
1303 // Press Q
1304 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1305 WmKeyDownInfo{kVirtualKeyQ, kScanCodeKeyQ, kNotExtended, kWasUp}.Build(
1307 WmCharInfo{'@', kScanCodeKeyQ, kNotExtended, kWasUp}.Build(
1308 kWmResultZero)});
1309
1310 EXPECT_EQ(tester.key_calls.size(), 2);
1312 kPhysicalKeyQ, kLogicalKeyQ, "@", kNotSynthesized);
1313 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"@");
1314 tester.clear_key_calls();
1315 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1316
1317 // Release Q
1318 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1319 WmKeyUpInfo{kVirtualKeyQ, kScanCodeKeyQ, kNotExtended}.Build(
1320 kWmResultZero)});
1321
1322 EXPECT_EQ(tester.key_calls.size(), 1);
1323 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1324 kPhysicalKeyQ, kLogicalKeyQ, "", kNotSynthesized);
1325 tester.clear_key_calls();
1326 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1327
1328 // Release AltGr. Win32 doesn't dispatch ControlLeft up. Instead Flutter will
1329 // forge one. The AltGr is a system key, therefore will be handled by Win32's
1330 // default WndProc.
1331 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1332 KeyStateChange{VK_LCONTROL, false, true},
1333 ExpectForgedMessage{
1334 WmKeyUpInfo{VK_CONTROL, kScanCodeControl, kNotExtended}.Build(
1335 kWmResultZero)},
1336 KeyStateChange{VK_RMENU, false, true},
1337 WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kExtended}.Build(
1339
1340 EXPECT_EQ(tester.key_calls.size(), 2);
1341 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1342 kPhysicalControlLeft, kLogicalControlLeft, "",
1343 kNotSynthesized);
1344 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
1345 kPhysicalAltRight, kLogicalAltRight, "",
1346 kNotSynthesized);
1347 tester.clear_key_calls();
1348 // The sys key up must not be redispatched. The forged ControlLeft up will.
1349 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1350}
1351
1352// Test the following two key sequences at the same time:
1353//
1354// 1. Tap AltGr, then tap AltGr.
1355// 2. Tap AltGr, hold CtrlLeft, tap AltGr, release CtrlLeft.
1356//
1357// The two sequences are indistinguishable until the very end when a CtrlLeft
1358// up event might or might not follow.
1359//
1360// Sequence 1: CtrlLeft down, AltRight down, AltRight up
1361// Sequence 2: CtrlLeft down, AltRight down, AltRight up, CtrlLeft up
1362//
1363// This is because pressing AltGr alone causes Win32 to send a fake "CtrlLeft
1364// down" event first (see |IsKeyDownAltRight| for detailed explanation).
1365TEST_F(KeyboardTest, AltGrTwice) {
1366 KeyboardTester tester{GetContext()};
1367 tester.Responding(false);
1368
1369 // 1. AltGr down.
1370
1371 // The key down event causes a ControlLeft down and a AltRight (extended
1372 // AltLeft) down.
1373 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1374 KeyStateChange{VK_LCONTROL, true, true},
1375 WmKeyDownInfo{VK_CONTROL, kScanCodeControl, kNotExtended, kWasUp}.Build(
1377 KeyStateChange{VK_RMENU, true, true},
1378 WmKeyDownInfo{VK_MENU, kScanCodeAlt, kExtended, kWasUp}.Build(
1379 kWmResultZero)});
1380
1381 EXPECT_EQ(tester.key_calls.size(), 2);
1383 kPhysicalControlLeft, kLogicalControlLeft, "",
1384 kNotSynthesized);
1386 kPhysicalAltRight, kLogicalAltRight, "",
1387 kNotSynthesized);
1388 tester.clear_key_calls();
1389 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1390
1391 // 2. AltGr up.
1392
1393 // The key up event only causes a AltRight (extended AltLeft) up.
1394 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1395 KeyStateChange{VK_LCONTROL, false, true},
1396 ExpectForgedMessage{
1397 WmKeyUpInfo{VK_CONTROL, kScanCodeControl, kNotExtended}.Build(
1398 kWmResultZero)},
1399 KeyStateChange{VK_RMENU, false, true},
1400 WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kExtended}.Build(
1402 EXPECT_EQ(tester.key_calls.size(), 2);
1403 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1404 kPhysicalControlLeft, kLogicalControlLeft, "",
1405 kNotSynthesized);
1406 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
1407 kPhysicalAltRight, kLogicalAltRight, "",
1408 kNotSynthesized);
1409 tester.clear_key_calls();
1410 // The sys key up must not be redispatched. The forged ControlLeft up will.
1411 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1412
1413 // 3. AltGr down (or: ControlLeft down then AltRight down.)
1414
1415 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1416 KeyStateChange{VK_LCONTROL, true, false},
1417 WmKeyDownInfo{VK_CONTROL, kScanCodeControl, kNotExtended, kWasUp}.Build(
1419 KeyStateChange{VK_RMENU, true, true},
1420 WmKeyDownInfo{VK_MENU, kScanCodeAlt, kExtended, kWasUp}.Build(
1421 kWmResultZero)});
1422
1423 EXPECT_EQ(tester.key_calls.size(), 2);
1425 kPhysicalControlLeft, kLogicalControlLeft, "",
1426 kNotSynthesized);
1428 kPhysicalAltRight, kLogicalAltRight, "",
1429 kNotSynthesized);
1430 tester.clear_key_calls();
1431 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1432
1433 // 4. AltGr up.
1434
1435 // The key up event only causes a AltRight (extended AltLeft) up.
1436 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1437 KeyStateChange{VK_LCONTROL, false, false},
1438 ExpectForgedMessage{
1439 WmKeyUpInfo{VK_CONTROL, kScanCodeControl, kNotExtended}.Build(
1440 kWmResultZero)},
1441 KeyStateChange{VK_RMENU, false, false},
1442 WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kExtended}.Build(
1444 EXPECT_EQ(tester.key_calls.size(), 2);
1445 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1446 kPhysicalControlLeft, kLogicalControlLeft, "",
1447 kNotSynthesized);
1448 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
1449 kPhysicalAltRight, kLogicalAltRight, "",
1450 kNotSynthesized);
1451 tester.clear_key_calls();
1452 // The sys key up must not be redispatched. The forged ControlLeft up will.
1453 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1454
1455 // 5. For key sequence 2: a real ControlLeft up.
1456 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1457 WmKeyUpInfo{VK_CONTROL, kScanCodeControl, kNotExtended}.Build(
1458 kWmResultZero)});
1459 EXPECT_EQ(tester.key_calls.size(), 1);
1460 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeDown, 0, 0, "",
1461 kNotSynthesized);
1462 tester.clear_key_calls();
1463 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
1464}
1465
1466// This tests dead key ^ then E on a French keyboard, which should be combined
1467// into ê.
1468TEST_F(KeyboardTest, DeadKeyThatCombines) {
1469 KeyboardTester tester{GetContext()};
1470 tester.Responding(false);
1471
1472 tester.SetLayout(LayoutFrench);
1473
1474 // Press ^¨ (US: Left bracket)
1475 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1476 WmKeyDownInfo{0xDD, kScanCodeBracketLeft, kNotExtended, kWasUp}.Build(
1478 WmDeadCharInfo{'^', kScanCodeBracketLeft, kNotExtended, kWasUp}.Build(
1479 kWmResultZero)});
1480
1481 EXPECT_EQ(tester.key_calls.size(), 1);
1483 kPhysicalBracketLeft, kLogicalBracketRight, "^",
1484 kNotSynthesized);
1485 tester.clear_key_calls();
1486 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1487
1488 // Release ^¨
1489 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1490 WmKeyUpInfo{0xDD, kScanCodeBracketLeft, kNotExtended}.Build(
1491 kWmResultZero)});
1492
1493 EXPECT_EQ(tester.key_calls.size(), 1);
1494 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1495 kPhysicalBracketLeft, kLogicalBracketRight, "",
1496 kNotSynthesized);
1497 tester.clear_key_calls();
1498 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1499
1500 // Press E
1501 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1502 WmKeyDownInfo{kVirtualKeyE, kScanCodeKeyE, kNotExtended, kWasUp}.Build(
1504 WmCharInfo{0xEA, kScanCodeKeyE, kNotExtended, kWasUp}.Build(
1505 kWmResultZero)});
1506
1507 EXPECT_EQ(tester.key_calls.size(), 2);
1509 kPhysicalKeyE, kLogicalKeyE, "ê", kNotSynthesized);
1510 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"ê");
1511 tester.clear_key_calls();
1512 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1513
1514 // Release E
1515 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1516 WmKeyUpInfo{kVirtualKeyE, kScanCodeKeyE, kNotExtended}.Build(
1517 kWmResultZero)});
1518
1519 EXPECT_EQ(tester.key_calls.size(), 1);
1520 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1521 kPhysicalKeyE, kLogicalKeyE, "", kNotSynthesized);
1522 tester.clear_key_calls();
1523 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1524}
1525
1526// This tests dead key ^ then E on a US INTL keyboard, which should be combined
1527// into ê.
1528//
1529// It is different from French AZERTY because the character that the ^ key is
1530// mapped to does not contain the dead key character somehow.
1531TEST_F(KeyboardTest, DeadKeyWithoutDeadMaskThatCombines) {
1532 KeyboardTester tester{GetContext()};
1533 tester.Responding(false);
1534
1535 // Press ShiftLeft
1536 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1537 KeyStateChange{VK_LSHIFT, true, true},
1539 kWmResultZero)});
1540
1541 EXPECT_EQ(tester.key_calls.size(), 1);
1543 kPhysicalShiftLeft, kLogicalShiftLeft, "",
1544 kNotSynthesized);
1545 tester.clear_key_calls();
1546 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1547
1548 // Press 6^
1549 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1550 WmKeyDownInfo{'6', kScanCodeDigit6, kNotExtended, kWasUp}.Build(
1552 WmDeadCharInfo{'^', kScanCodeDigit6, kNotExtended, kWasUp}.Build(
1553 kWmResultZero)});
1554
1555 EXPECT_EQ(tester.key_calls.size(), 1);
1557 kPhysicalDigit6, kLogicalDigit6, "6", kNotSynthesized);
1558 tester.clear_key_calls();
1559 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1560
1561 // Release 6^
1562 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1563 WmKeyUpInfo{'6', kScanCodeDigit6, kNotExtended}.Build(kWmResultZero)});
1564
1565 EXPECT_EQ(tester.key_calls.size(), 1);
1566 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1567 kPhysicalDigit6, kLogicalDigit6, "", kNotSynthesized);
1568 tester.clear_key_calls();
1569 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1570
1571 // Release ShiftLeft
1572 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1573 KeyStateChange{VK_LSHIFT, false, true},
1575 kWmResultZero)});
1576
1577 EXPECT_EQ(tester.key_calls.size(), 1);
1578 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1579 kPhysicalShiftLeft, kLogicalShiftLeft, "",
1580 kNotSynthesized);
1581 tester.clear_key_calls();
1582 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1583
1584 // Press E
1585 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1586 WmKeyDownInfo{kVirtualKeyE, kScanCodeKeyE, kNotExtended, kWasUp}.Build(
1588 WmCharInfo{0xEA, kScanCodeKeyE, kNotExtended, kWasUp}.Build(
1589 kWmResultZero)});
1590
1591 EXPECT_EQ(tester.key_calls.size(), 2);
1593 kPhysicalKeyE, kLogicalKeyE, "ê", kNotSynthesized);
1594 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"ê");
1595 tester.clear_key_calls();
1596 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1597
1598 // Release E
1599 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1600 WmKeyUpInfo{kVirtualKeyE, kScanCodeKeyE, kNotExtended}.Build(
1601 kWmResultZero)});
1602
1603 EXPECT_EQ(tester.key_calls.size(), 1);
1604 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1605 kPhysicalKeyE, kLogicalKeyE, "", kNotSynthesized);
1606 tester.clear_key_calls();
1607 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1608}
1609
1610// This tests dead key ^ then & (US: 1) on a French keyboard, which do not
1611// combine and should output "^&".
1612TEST_F(KeyboardTest, DeadKeyThatDoesNotCombine) {
1613 KeyboardTester tester{GetContext()};
1614 tester.Responding(false);
1615
1616 tester.SetLayout(LayoutFrench);
1617
1618 // Press ^¨ (US: Left bracket)
1619 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1620 WmKeyDownInfo{0xDD, kScanCodeBracketLeft, kNotExtended, kWasUp}.Build(
1622 WmDeadCharInfo{'^', kScanCodeBracketLeft, kNotExtended, kWasUp}.Build(
1623 kWmResultZero)});
1624
1625 EXPECT_EQ(tester.key_calls.size(), 1);
1627 kPhysicalBracketLeft, kLogicalBracketRight, "^",
1628 kNotSynthesized);
1629 tester.clear_key_calls();
1630 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1631
1632 // Release ^¨
1633 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1634 WmKeyUpInfo{0xDD, kScanCodeBracketLeft, kNotExtended}.Build(
1635 kWmResultZero)});
1636
1637 EXPECT_EQ(tester.key_calls.size(), 1);
1638 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1639 kPhysicalBracketLeft, kLogicalBracketRight, "",
1640 kNotSynthesized);
1641 tester.clear_key_calls();
1642 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1643
1644 // Press 1
1645 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1646 WmKeyDownInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended, kWasUp}
1648 WmCharInfo{'^', kScanCodeDigit1, kNotExtended, kWasUp}.Build(
1650 WmCharInfo{'&', kScanCodeDigit1, kNotExtended, kWasUp}.Build(
1651 kWmResultZero)});
1652
1653 EXPECT_EQ(tester.key_calls.size(), 3);
1655 kPhysicalDigit1, kLogicalDigit1, "^", kNotSynthesized);
1656 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"^");
1657 EXPECT_CALL_IS_TEXT(tester.key_calls[2], u"&");
1658 tester.clear_key_calls();
1659 // TODO(dkwingsmt): This count should probably be 3. Currently the '^'
1660 // message is redispatched due to being part of the KeyDown session, which is
1661 // not handled by the framework, while the '&' message is not redispatched
1662 // for being a standalone message. We should resolve this inconsistency.
1663 // https://github.com/flutter/flutter/issues/98306
1664 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1665
1666 // Release 1
1667 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1668 WmKeyUpInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended}.Build(
1669 kWmResultZero)});
1670
1671 EXPECT_EQ(tester.key_calls.size(), 1);
1672 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1673 kPhysicalDigit1, kLogicalDigit1, "", kNotSynthesized);
1674 tester.clear_key_calls();
1675 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1676}
1677
1678// This tests dead key `, then dead key `, then e.
1679//
1680// It should output ``e, instead of `è.
1681TEST_F(KeyboardTest, DeadKeyTwiceThenLetter) {
1682 KeyboardTester tester{GetContext()};
1683 tester.Responding(false);
1684
1685 // US INTL layout.
1686
1687 // Press `
1688 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1689 WmKeyDownInfo{0xC0, kScanCodeBackquote, kNotExtended, kWasUp}.Build(
1691 WmDeadCharInfo{'`', kScanCodeBackquote, kNotExtended, kWasUp}.Build(
1692 kWmResultZero)});
1693
1694 EXPECT_EQ(tester.key_calls.size(), 1);
1696 kPhysicalBackquote, kLogicalBackquote, "`",
1697 kNotSynthesized);
1698 tester.clear_key_calls();
1699 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1700
1701 // Release `
1702 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1703 WmKeyUpInfo{0xC0, kScanCodeBackquote, kNotExtended}.Build(
1704 kWmResultZero)});
1705
1706 EXPECT_EQ(tester.key_calls.size(), 1);
1707 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1708 kPhysicalBackquote, kLogicalBackquote, "",
1709 kNotSynthesized);
1710 tester.clear_key_calls();
1711 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1712
1713 // Press ` again.
1714 // The response should be slow.
1715 std::vector<MockKeyResponseController::ResponseCallback> recorded_callbacks;
1716 tester.LateResponding(
1717 [&recorded_callbacks](
1718 const FlutterKeyEvent* event,
1720 recorded_callbacks.push_back(callback);
1721 });
1722
1723 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1724 WmKeyDownInfo{0xC0, kScanCodeBackquote, kNotExtended, kWasUp}.Build(
1726 WmCharInfo{'`', kScanCodeBackquote, kNotExtended, kWasUp, kBeingReleased,
1727 kNoContext, 1, /*bit25*/ true}
1729 WmCharInfo{'`', kScanCodeBackquote, kNotExtended, kWasUp}.Build(
1730 kWmResultZero)});
1731
1732 EXPECT_EQ(recorded_callbacks.size(), 1);
1733 EXPECT_EQ(tester.key_calls.size(), 1);
1735 kPhysicalBackquote, kLogicalBackquote, "`",
1736 kNotSynthesized);
1737 tester.clear_key_calls();
1738 // Key down event responded with false.
1739 recorded_callbacks.front()(false);
1740 EXPECT_EQ(tester.key_calls.size(), 2);
1741 EXPECT_CALL_IS_TEXT(tester.key_calls[0], u"`");
1742 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"`");
1743 tester.clear_key_calls();
1744 // TODO(dkwingsmt): This count should probably be 3. See the comment above
1745 // that is marked with the same issue.
1746 // https://github.com/flutter/flutter/issues/98306
1747 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1748
1749 tester.Responding(false);
1750
1751 // Release `
1752 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1753 WmKeyUpInfo{0xC0, kScanCodeBackquote, kNotExtended}.Build(
1754 kWmResultZero)});
1755
1756 EXPECT_EQ(tester.key_calls.size(), 1);
1757 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1758 kPhysicalBackquote, kLogicalBackquote, "",
1759 kNotSynthesized);
1760 tester.clear_key_calls();
1761 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1762}
1763
1764// This tests when the resulting character needs to be combined with surrogates.
1765TEST_F(KeyboardTest, MultibyteCharacter) {
1766 KeyboardTester tester{GetContext()};
1767 tester.Responding(false);
1768
1769 // Gothic Keyboard layout. (We need a layout that yields non-BMP characters
1770 // without IME, which is actually very rare.)
1771
1772 // Press key W of a US keyboard, which should yield character '𐍅'.
1773 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1774 WmKeyDownInfo{kVirtualKeyW, kScanCodeKeyW, kNotExtended, kWasUp}.Build(
1776 WmCharInfo{0xd800, kScanCodeKeyW, kNotExtended, kWasUp}.Build(
1778 WmCharInfo{0xdf45, kScanCodeKeyW, kNotExtended, kWasUp}.Build(
1779 kWmResultZero)});
1780
1781 const char* st = tester.key_calls[0].key_event.character;
1782
1783 EXPECT_EQ(tester.key_calls.size(), 2);
1785 kPhysicalKeyW, kLogicalKeyW, "𐍅", kNotSynthesized);
1786 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"𐍅");
1787 tester.clear_key_calls();
1788 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 3);
1789
1790 // Release W
1791 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1792 WmKeyUpInfo{kVirtualKeyW, kScanCodeKeyW, kNotExtended}.Build(
1793 kWmResultZero)});
1794
1795 EXPECT_EQ(tester.key_calls.size(), 1);
1796 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1797 kPhysicalKeyW, kLogicalKeyW, "", kNotSynthesized);
1798 tester.clear_key_calls();
1799 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1800}
1801
1802TEST_F(KeyboardTest, SynthesizeModifiers) {
1803 KeyboardTester tester{GetContext()};
1804 tester.Responding(false);
1805
1806 // Two dummy events used to trigger synthesization.
1807 Win32Message event1 =
1808 WmKeyDownInfo{VK_BACK, kScanCodeBackspace, kNotExtended, kWasUp}.Build(
1810 Win32Message event2 =
1811 WmKeyUpInfo{VK_BACK, kScanCodeBackspace, kNotExtended}.Build(
1813
1814 // ShiftLeft
1815 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1816 KeyStateChange{VK_LSHIFT, true, true}, event1});
1817 EXPECT_EQ(tester.key_calls.size(), 2);
1819 kPhysicalShiftLeft, kLogicalShiftLeft, "", kSynthesized);
1820 tester.clear_key_calls();
1821 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1822
1823 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1824 KeyStateChange{VK_LSHIFT, false, true}, event2});
1825 EXPECT_EQ(tester.key_calls.size(), 2);
1826 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1827 kPhysicalShiftLeft, kLogicalShiftLeft, "", kSynthesized);
1828 tester.clear_key_calls();
1829 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1830
1831 // ShiftRight
1832 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1833 KeyStateChange{VK_RSHIFT, true, true}, event1});
1834 EXPECT_EQ(tester.key_calls.size(), 2);
1836 kPhysicalShiftRight, kLogicalShiftRight, "",
1837 kSynthesized);
1838 tester.clear_key_calls();
1839 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1840
1841 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1842 KeyStateChange{VK_RSHIFT, false, true}, event2});
1843 EXPECT_EQ(tester.key_calls.size(), 2);
1844 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1845 kPhysicalShiftRight, kLogicalShiftRight, "",
1846 kSynthesized);
1847 tester.clear_key_calls();
1848 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1849
1850 // ControlLeft
1851 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1852 KeyStateChange{VK_LCONTROL, true, true}, event1});
1853 EXPECT_EQ(tester.key_calls.size(), 2);
1855 kPhysicalControlLeft, kLogicalControlLeft, "",
1856 kSynthesized);
1857 tester.clear_key_calls();
1858 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1859
1860 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1861 KeyStateChange{VK_LCONTROL, false, true}, event2});
1862 EXPECT_EQ(tester.key_calls.size(), 2);
1863 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1864 kPhysicalControlLeft, kLogicalControlLeft, "",
1865 kSynthesized);
1866 tester.clear_key_calls();
1867 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1868
1869 // ControlRight
1870 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1871 KeyStateChange{VK_RCONTROL, true, true}, event1});
1872 EXPECT_EQ(tester.key_calls.size(), 2);
1874 kPhysicalControlRight, kLogicalControlRight, "",
1875 kSynthesized);
1876 tester.clear_key_calls();
1877 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1878
1879 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1880 KeyStateChange{VK_RCONTROL, false, true}, event2});
1881 EXPECT_EQ(tester.key_calls.size(), 2);
1882 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1883 kPhysicalControlRight, kLogicalControlRight, "",
1884 kSynthesized);
1885 tester.clear_key_calls();
1886 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1887
1888 // AltLeft
1889 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1890 KeyStateChange{VK_LMENU, true, true}, event1});
1891 EXPECT_EQ(tester.key_calls.size(), 2);
1893 kPhysicalAltLeft, kLogicalAltLeft, "", kSynthesized);
1894 tester.clear_key_calls();
1895 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1896
1897 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1898 KeyStateChange{VK_LMENU, false, true}, event2});
1899 EXPECT_EQ(tester.key_calls.size(), 2);
1900 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1901 kPhysicalAltLeft, kLogicalAltLeft, "", kSynthesized);
1902 tester.clear_key_calls();
1903 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1904
1905 // AltRight
1906 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1907 KeyStateChange{VK_RMENU, true, true}, event1});
1908 EXPECT_EQ(tester.key_calls.size(), 2);
1910 kPhysicalAltRight, kLogicalAltRight, "", kSynthesized);
1911 tester.clear_key_calls();
1912 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1913
1914 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1915 KeyStateChange{VK_RMENU, false, true}, event2});
1916 EXPECT_EQ(tester.key_calls.size(), 2);
1917 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1918 kPhysicalAltRight, kLogicalAltRight, "", kSynthesized);
1919 tester.clear_key_calls();
1920 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1921
1922 // MetaLeft
1923 tester.InjectKeyboardChanges(
1924 std::vector<KeyboardChange>{KeyStateChange{VK_LWIN, true, true}, event1});
1925 EXPECT_EQ(tester.key_calls.size(), 2);
1927 kPhysicalMetaLeft, kLogicalMetaLeft, "", kSynthesized);
1928 tester.clear_key_calls();
1929 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1930
1931 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1932 KeyStateChange{VK_LWIN, false, true}, event2});
1933 EXPECT_EQ(tester.key_calls.size(), 2);
1934 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1935 kPhysicalMetaLeft, kLogicalMetaLeft, "", kSynthesized);
1936 tester.clear_key_calls();
1937 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1938
1939 // MetaRight
1940 tester.InjectKeyboardChanges(
1941 std::vector<KeyboardChange>{KeyStateChange{VK_RWIN, true, true}, event1});
1942 EXPECT_EQ(tester.key_calls.size(), 2);
1944 kPhysicalMetaRight, kLogicalMetaRight, "", kSynthesized);
1945 tester.clear_key_calls();
1946 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1947
1948 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1949 KeyStateChange{VK_RWIN, false, true}, event2});
1950 EXPECT_EQ(tester.key_calls.size(), 2);
1951 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1952 kPhysicalMetaRight, kLogicalMetaRight, "", kSynthesized);
1953 tester.clear_key_calls();
1954 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1955
1956 // CapsLock, phase 0 -> 2 -> 0.
1957 // (For phases, see |SynchronizeCriticalToggledStates|.)
1958 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1959 KeyStateChange{VK_CAPITAL, false, true}, event1});
1960 EXPECT_EQ(tester.key_calls.size(), 3);
1962 kPhysicalCapsLock, kLogicalCapsLock, "", kSynthesized);
1963 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
1964 kPhysicalCapsLock, kLogicalCapsLock, "", kSynthesized);
1965 tester.clear_key_calls();
1966 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1967
1968 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1969 KeyStateChange{VK_CAPITAL, false, false}, event2});
1970 EXPECT_EQ(tester.key_calls.size(), 3);
1972 kPhysicalCapsLock, kLogicalCapsLock, "", kSynthesized);
1973 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
1974 kPhysicalCapsLock, kLogicalCapsLock, "", kSynthesized);
1975 tester.clear_key_calls();
1976 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1977
1978 // ScrollLock, phase 0 -> 1 -> 3
1979 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1980 KeyStateChange{VK_SCROLL, true, true}, event1});
1981 EXPECT_EQ(tester.key_calls.size(), 2);
1983 kPhysicalScrollLock, kLogicalScrollLock, "",
1984 kSynthesized);
1985 tester.clear_key_calls();
1986 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1987
1988 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1989 KeyStateChange{VK_SCROLL, true, false}, event2});
1990 EXPECT_EQ(tester.key_calls.size(), 3);
1991 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1992 kPhysicalScrollLock, kLogicalScrollLock, "",
1993 kSynthesized);
1995 kPhysicalScrollLock, kLogicalScrollLock, "",
1996 kSynthesized);
1997 tester.clear_key_calls();
1998 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1999
2000 // NumLock, phase 0 -> 3 -> 2
2001 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2002 KeyStateChange{VK_NUMLOCK, true, false}, event1});
2003 // TODO(dkwingsmt): Synthesizing from phase 0 to 3 should yield a full key
2004 // tap and a key down. Fix the algorithm so that the following result becomes
2005 // 4 keycalls with an extra pair of key down and up.
2006 // https://github.com/flutter/flutter/issues/98533
2007 EXPECT_EQ(tester.key_calls.size(), 2);
2009 kPhysicalNumLock, kLogicalNumLock, "", kSynthesized);
2010 tester.clear_key_calls();
2011 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
2012
2013 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2014 KeyStateChange{VK_NUMLOCK, false, true}, event2});
2015 EXPECT_EQ(tester.key_calls.size(), 4);
2016 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
2017 kPhysicalNumLock, kLogicalNumLock, "", kSynthesized);
2019 kPhysicalNumLock, kLogicalNumLock, "", kSynthesized);
2020 EXPECT_CALL_IS_EVENT(tester.key_calls[2], kFlutterKeyEventTypeUp,
2021 kPhysicalNumLock, kLogicalNumLock, "", kSynthesized);
2022 tester.clear_key_calls();
2023 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
2024}
2025
2026// Pressing extended keys during IME events should work properly by not sending
2027// any events.
2028//
2029// Regression test for https://github.com/flutter/flutter/issues/95888 .
2030TEST_F(KeyboardTest, ImeExtendedEventsAreIgnored) {
2031 KeyboardTester tester{GetContext()};
2032 tester.Responding(false);
2033
2034 // US Keyboard layout.
2035
2036 // There should be preceding key events to make the keyboard into IME mode.
2037 // Omit them in this test since they are not relavent.
2038
2039 // Press CtrlRight in IME mode.
2040 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2041 KeyStateChange{VK_RCONTROL, true, false},
2042 WmKeyDownInfo{VK_PROCESSKEY, kScanCodeControl, kExtended, kWasUp}.Build(
2043 kWmResultZero)});
2044
2045 EXPECT_EQ(tester.key_calls.size(), 1);
2046 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeDown, 0, 0, "",
2047 kNotSynthesized);
2048 tester.clear_key_calls();
2049 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2050}
2051
2052// Ensures that synthesization works correctly when a Shift key is pressed and
2053// (only) its up event is labeled as an IME event (VK_PROCESSKEY).
2054//
2055// Regression test for https://github.com/flutter/flutter/issues/104169. These
2056// are real messages recorded when pressing Shift-2 using Microsoft Pinyin IME
2057// on Win 10 Enterprise, which crashed the app before the fix.
2058TEST_F(KeyboardTest, UpOnlyImeEventsAreCorrectlyHandled) {
2059 KeyboardTester tester{GetContext()};
2060 tester.Responding(true);
2061
2062 // US Keyboard layout.
2063
2064 // Press CtrlRight in IME mode.
2065 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2066 KeyStateChange{VK_LSHIFT, true, false},
2069 WmKeyDownInfo{VK_PROCESSKEY, kScanCodeDigit2, kNotExtended, kWasUp}.Build(
2071 KeyStateChange{VK_LSHIFT, false, true},
2074 WmKeyUpInfo{'2', kScanCodeDigit2, kNotExtended, kWasUp}.Build(
2075 kWmResultZero)});
2076
2077 EXPECT_EQ(tester.key_calls.size(), 4);
2079 kPhysicalShiftLeft, kLogicalShiftLeft, "",
2080 kNotSynthesized);
2081 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeDown, 0, 0, "",
2082 kNotSynthesized);
2083 EXPECT_CALL_IS_EVENT(tester.key_calls[2], kFlutterKeyEventTypeUp,
2084 kPhysicalShiftLeft, kLogicalShiftLeft, "",
2085 kNotSynthesized);
2086 EXPECT_CALL_IS_EVENT(tester.key_calls[3], kFlutterKeyEventTypeDown, 0, 0, "",
2087 kNotSynthesized);
2088 tester.clear_key_calls();
2089}
2090
2091// Regression test for a crash in an earlier implementation.
2092//
2093// In real life, the framework responds slowly. The next real event might
2094// arrive earlier than the framework response, and if the 2nd event has an
2095// identical hash as the one waiting for response, an earlier implementation
2096// will crash upon the response.
2097TEST_F(KeyboardTest, SlowFrameworkResponse) {
2098 KeyboardTester tester{GetContext()};
2099
2100 std::vector<MockKeyResponseController::ResponseCallback> recorded_callbacks;
2101
2102 // Store callbacks to manually call them.
2103 tester.LateResponding(
2104 [&recorded_callbacks](
2105 const FlutterKeyEvent* event,
2107 recorded_callbacks.push_back(callback);
2108 });
2109
2110 // Press A
2111 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2115 kWmResultZero)});
2116
2117 // Hold A
2118 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2122 kWmResultZero)});
2123
2124 EXPECT_EQ(tester.key_calls.size(), 1);
2126 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
2127 EXPECT_EQ(recorded_callbacks.size(), 1);
2128 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2129
2130 // The first response.
2131 recorded_callbacks.front()(false);
2132
2133 EXPECT_EQ(tester.key_calls.size(), 3);
2134 EXPECT_EQ(recorded_callbacks.size(), 2);
2135 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"a");
2137 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
2138 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
2139
2140 // The second response.
2141 recorded_callbacks.back()(false);
2142
2143 EXPECT_EQ(tester.key_calls.size(), 4);
2144 EXPECT_CALL_IS_TEXT(tester.key_calls[3], u"a");
2145 tester.clear_key_calls();
2146 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
2147}
2148
2149// Regression test for https://github.com/flutter/flutter/issues/84210.
2150//
2151// When the framework response is slow during a sequence of identical messages,
2152// make sure the real messages are not mistaken as redispatched messages,
2153// in order to not mess up the order of events.
2154//
2155// In this test we use:
2156//
2157// KeyA down, KeyA up, (down event responded with false), KeyA down, KeyA up,
2158//
2159// The code must not take the 2nd real key down events as a redispatched event.
2160TEST_F(KeyboardTest, SlowFrameworkResponseForIdenticalEvents) {
2161 KeyboardTester tester{GetContext()};
2162 std::vector<MockKeyResponseController::ResponseCallback> recorded_callbacks;
2163
2164 // Store callbacks to manually call them.
2165 tester.LateResponding(
2166 [&recorded_callbacks](
2167 const FlutterKeyEvent* event,
2169 recorded_callbacks.push_back(callback);
2170 });
2171
2172 // Press A
2173 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2177 kWmResultZero)});
2178
2179 EXPECT_EQ(tester.key_calls.size(), 1);
2181 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
2182 tester.clear_key_calls();
2183 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2184
2185 // Release A
2186 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2188 kWmResultZero)});
2189
2190 EXPECT_EQ(tester.key_calls.size(), 0);
2191 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2192
2193 // The first down event responded with false.
2194 EXPECT_EQ(recorded_callbacks.size(), 1);
2195 recorded_callbacks.front()(false);
2196
2197 EXPECT_EQ(tester.key_calls.size(), 2);
2198 EXPECT_CALL_IS_TEXT(tester.key_calls[0], u"a");
2199 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
2200 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
2201 tester.clear_key_calls();
2202 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
2203
2204 // Press A again
2205 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2209 kWmResultZero)});
2210
2211 // Nothing more was dispatched because the first up event hasn't been
2212 // responded yet.
2213 EXPECT_EQ(recorded_callbacks.size(), 2);
2214 EXPECT_EQ(tester.key_calls.size(), 0);
2215 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2216
2217 // The first up event responded with false, which was redispatched, and caused
2218 // the down event to be dispatched.
2219 recorded_callbacks.back()(false);
2220 EXPECT_EQ(tester.key_calls.size(), 1);
2222 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
2223 tester.clear_key_calls();
2224 EXPECT_EQ(recorded_callbacks.size(), 3);
2225 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
2226
2227 // Release A again
2228 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2230 kWmResultZero)});
2231
2232 EXPECT_EQ(tester.key_calls.size(), 0);
2233 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2234}
2235
2236TEST_F(KeyboardTest, TextInputSubmit) {
2237 KeyboardTester tester{GetContext()};
2238 tester.Responding(false);
2239
2240 // US Keyboard layout
2241
2242 tester.InjectPlatformMessage(
2243 "flutter/textinput", "TextInput.setClient",
2244 R"|([108, {"inputAction": "TextInputAction.none", "viewId": 0}])|");
2245
2246 // Press Enter
2247 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2248 WmKeyDownInfo{VK_RETURN, kScanCodeEnter, kNotExtended, kWasUp}.Build(
2250 WmCharInfo{'\n', kScanCodeEnter, kNotExtended, kWasUp}.Build(
2251 kWmResultZero)});
2252
2253 EXPECT_EQ(tester.key_calls.size(), 2);
2255 kPhysicalEnter, kLogicalEnter, "", kNotSynthesized);
2257 tester.key_calls[1],
2258 "{"
2259 R"|("method":"TextInputClient.performAction",)|"
2260 R"|("args":[108,"TextInputAction.none"])|"
2261 "}");
2262 tester.clear_key_calls();
2263 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
2264
2265 // Release Enter
2266 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2267 WmKeyUpInfo{VK_RETURN, kScanCodeEnter, kNotExtended}.Build(
2268 kWmResultZero)});
2269
2270 EXPECT_EQ(tester.key_calls.size(), 1);
2271 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
2272 kPhysicalEnter, kLogicalEnter, "", kNotSynthesized);
2273 tester.clear_key_calls();
2274 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
2275
2276 // Make sure OnText is not obstructed after pressing Enter.
2277 //
2278 // Regression test for https://github.com/flutter/flutter/issues/97706.
2279
2280 // Press A
2281 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2285 kWmResultZero)});
2286
2287 EXPECT_EQ(tester.key_calls.size(), 2);
2289 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
2290 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"a");
2291 tester.clear_key_calls();
2292
2293 // Release A
2294 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2296 kWmResultZero)});
2297
2298 EXPECT_EQ(tester.key_calls.size(), 1);
2299 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
2300 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
2301 tester.clear_key_calls();
2302}
2303
2304TEST_F(KeyboardTest, VietnameseTelexAddDiacriticWithFastResponse) {
2305 // In this test, the user presses the folloing keys:
2306 //
2307 // Key Current text
2308 // ===========================
2309 // A a
2310 // F à
2311 //
2312 // And the Backspace event is responded immediately.
2313
2314 KeyboardTester tester{GetContext()};
2315 tester.Responding(false);
2316
2317 // US Keyboard layout
2318
2319 // Press A
2320 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2324 kWmResultZero)});
2325
2326 EXPECT_EQ(tester.key_calls.size(), 2);
2328 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
2329 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"a");
2330 tester.clear_key_calls();
2331 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
2332
2333 // Release A
2334 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2336 kWmResultZero)});
2337
2338 EXPECT_EQ(tester.key_calls.size(), 1);
2339 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
2340 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
2341 tester.clear_key_calls();
2342 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
2343
2344 // Press F, which is translated to:
2345 //
2346 // Backspace down, char & up, then VK_PACKET('à').
2347 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2348 WmKeyDownInfo{VK_BACK, kScanCodeBackspace, kNotExtended, kWasUp}.Build(
2350 WmCharInfo{0x8, kScanCodeBackspace, kNotExtended, kWasUp}.Build(
2352 WmKeyUpInfo{VK_BACK, kScanCodeBackspace, kNotExtended}.Build(
2355 WmCharInfo{0xe0 /*'à'*/, 0, kNotExtended, kWasUp}.Build(kWmResultZero),
2357
2358 EXPECT_EQ(tester.key_calls.size(), 3);
2360 kPhysicalBackspace, kLogicalBackspace, "",
2361 kNotSynthesized);
2362 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
2363 kPhysicalBackspace, kLogicalBackspace, "",
2364 kNotSynthesized);
2365 EXPECT_CALL_IS_TEXT(tester.key_calls[2], u"à");
2366 tester.clear_key_calls();
2367 // TODO(dkwingsmt): This count should probably be 4. Currently the CHAR 0x8
2368 // message is redispatched due to being part of the KeyDown session, which is
2369 // not handled by the framework, while the 'à' message is not redispatched
2370 // for being a standalone message. We should resolve this inconsistency.
2371 // https://github.com/flutter/flutter/issues/98306
2372 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 3);
2373
2374 // Release F
2375 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2376 WmKeyUpInfo{kVirtualKeyF, kScanCodeKeyF, kNotExtended,
2377 /* overwrite_prev_state_0 */ true}
2378 .Build(kWmResultZero)});
2379
2380 EXPECT_EQ(tester.key_calls.size(), 1);
2381 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeDown, 0, 0, "",
2382 kNotSynthesized);
2383 tester.clear_key_calls();
2384 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2385}
2386
2388 bool backspace_response) {
2389 // In this test, the user presses the following keys:
2390 //
2391 // Key Current text
2392 // ===========================
2393 // A a
2394 // F à
2395 //
2396 // And the Backspace down event is responded slowly with `backspace_response`.
2397
2398 KeyboardTester tester{context};
2399 tester.Responding(false);
2400
2401 // US Keyboard layout
2402
2403 // Press A
2404 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2408 kWmResultZero)});
2409
2410 EXPECT_EQ(tester.key_calls.size(), 2);
2412 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
2413 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"a");
2414 tester.clear_key_calls();
2415 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
2416
2417 // Release A
2418 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2420 kWmResultZero)});
2421
2422 EXPECT_EQ(tester.key_calls.size(), 1);
2423 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
2424 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
2425 tester.clear_key_calls();
2426 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
2427
2428 std::vector<MockKeyResponseController::ResponseCallback> recorded_callbacks;
2429 tester.LateResponding(
2430 [&recorded_callbacks](
2431 const FlutterKeyEvent* event,
2433 recorded_callbacks.push_back(callback);
2434 });
2435
2436 // Press F, which is translated to:
2437 //
2438 // Backspace down, char & up, VK_PACKET('à').
2439 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2440 WmKeyDownInfo{VK_BACK, kScanCodeBackspace, kNotExtended, kWasUp}.Build(
2442 WmCharInfo{0x8, kScanCodeBackspace, kNotExtended, kWasUp}.Build(
2444 WmKeyUpInfo{VK_BACK, kScanCodeBackspace, kNotExtended}.Build(
2447 WmCharInfo{0xe0 /*'à'*/, 0, kNotExtended, kWasUp}.Build(kWmResultZero),
2449
2450 // The Backspace event has not responded yet, therefore the char message must
2451 // hold. This is because when the framework is handling the Backspace event,
2452 // it will send a setEditingState message that updates the text state that has
2453 // the last character deleted (denoted by `string1`). Processing the char
2454 // message before then will cause the final text to set to `string1`.
2455 EXPECT_EQ(tester.key_calls.size(), 1);
2457 kPhysicalBackspace, kLogicalBackspace, "",
2458 kNotSynthesized);
2459 tester.clear_key_calls();
2460 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2461
2462 EXPECT_EQ(recorded_callbacks.size(), 1);
2463 recorded_callbacks[0](backspace_response);
2464
2465 EXPECT_EQ(tester.key_calls.size(), 1);
2466 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
2467 kPhysicalBackspace, kLogicalBackspace, "",
2468 kNotSynthesized);
2469 tester.clear_key_calls();
2470 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(),
2471 backspace_response ? 0 : 2);
2472
2473 recorded_callbacks[1](false);
2474 EXPECT_EQ(tester.key_calls.size(), 1);
2475 EXPECT_CALL_IS_TEXT(tester.key_calls[0], u"à");
2476 tester.clear_key_calls();
2477 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
2478
2479 tester.Responding(false);
2480
2481 // Release F
2482 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2483 WmKeyUpInfo{kVirtualKeyF, kScanCodeKeyF, kNotExtended,
2484 /* overwrite_prev_state_0 */ true}
2485 .Build(kWmResultZero)});
2486
2487 EXPECT_EQ(tester.key_calls.size(), 1);
2488 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeDown, 0, 0, "",
2489 kNotSynthesized);
2490 tester.clear_key_calls();
2491 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2492}
2493
2494TEST_F(KeyboardTest, VietnameseTelexAddDiacriticWithSlowFalseResponse) {
2496}
2497
2498TEST_F(KeyboardTest, VietnameseTelexAddDiacriticWithSlowTrueResponse) {
2500}
2501
2502// Ensure that the scancode-less key events issued by Narrator
2503// when toggling caps lock don't violate assert statements.
2504TEST_F(KeyboardTest, DoubleCapsLock) {
2505 KeyboardTester tester{GetContext()};
2506 tester.Responding(false);
2507
2508 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2509 WmKeyDownInfo{VK_CAPITAL, 0, kNotExtended}.Build(),
2510 WmKeyUpInfo{VK_CAPITAL, 0, kNotExtended}.Build()});
2511
2512 tester.clear_key_calls();
2513}
2514
2515} // namespace testing
2516} // namespace flutter
GLenum type
static const JsonMessageCodec & GetInstance()
virtual void RedispatchEvent(std::unique_ptr< PendingEvent > event)
std::unique_ptr< std::vector< uint8_t > > EncodeMessage(const T &message) const
std::function< void(const FlutterKeyEvent *, ResponseCallback)> EmbedderCallbackHandler
std::function< void(bool)> ResponseCallback
BOOL Win32PeekMessage(LPMSG lpMsg, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg)
int32_t value
@ kFlutterKeyEventTypeDown
Definition embedder.h:1378
@ kFlutterKeyEventTypeUp
Definition embedder.h:1377
@ kFlutterKeyEventTypeRepeat
Definition embedder.h:1379
GLFWwindow * window
Definition main.cc:60
FlutterEngine engine
Definition main.cc:84
FlView * view
const char * message
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
const gchar * channel
const gchar FlBinaryMessengerMessageHandler handler
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
Definition logging.h:101
#define FML_DCHECK(condition)
Definition logging.h:122
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition macros.h:27
std::shared_ptr< ImpellerAllocator > allocator
CallbackHandler callback_handler
std::string text_method_call
bool pressed
FlutterKeyEvent key_event
std::u16string text
#define EXPECT_CALL_IS_TEXT(_key_call, u16_string)
#define EXPECT_CALL_IS_TEXT_METHOD_CALL(_key_call, json_string)
std::list< KeyStateChange > state_changes_afterwards
std::vector< KeyCall > key_calls
KeyStateChange key_state_change
bool toggled_on
union flutter::testing::@2836::KeyboardChange::@77 content
Win32Message expected_forged_message
#define EXPECT_CALL_IS_EVENT(_key_call, _type, _physical, _logical, _character, _synthesized)
TEST_F(DisplayListTest, Defaults)
constexpr LRESULT kWmResultZero
Definition wm_builders.h:14
constexpr LRESULT kWmResultDefault
Definition wm_builders.h:15
void MockEmbedderApiForKeyboard(EngineModifier &modifier, std::shared_ptr< MockKeyResponseController > response_controller)
constexpr LRESULT kWmResultDontCheck
Definition wm_builders.h:16
char * clone_string(const char *string)
void VietnameseTelexAddDiacriticWithSlowResponse(WindowsTestContext &context, bool backspace_response)
constexpr int64_t kImplicitViewId
constexpr int kScanCodeShiftRight
constexpr int kScanCodeShiftLeft
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
Definition switch_defs.h:98
Definition ref_ptr.h:261
const char * character
Definition embedder.h:1439
Win32Message Build(LRESULT expected_result=kWmResultDontCheck)
Win32Message Build(LRESULT expected_result=kWmResultDontCheck)
Win32Message Build(LRESULT expected_result=kWmResultDontCheck)
Win32Message Build(LRESULT expected_result=kWmResultDontCheck)
Win32Message Build(LRESULT expected_result=kWmResultDontCheck)
Win32Message Build(LRESULT expected_result=kWmResultDontCheck)
int BOOL
struct tagMSG * LPMSG
LONG_PTR LRESULT
unsigned int UINT
LONG_PTR LPARAM
short SHORT
UINT_PTR WPARAM