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, engine, std::move(window)) {}
344
345 void OnText(const std::u16string& text) override {
346 on_key_call_(KeyCall{
347 .type = KeyCall::kKeyCallOnText,
348 .text = text,
349 });
350 }
351
352 private:
353 std::function<void(KeyCall)> on_key_call_;
354
355 FML_DISALLOW_COPY_AND_ASSIGN(TestFlutterWindowsView);
356};
357
358class KeyboardTester {
359 public:
360 using ResponseHandler =
362
363 explicit KeyboardTester(WindowsTestContext& context)
364 : callback_handler_(RespondValue(false)),
365 map_virtual_key_layout_(LayoutDefault) {
366 engine_ = GetTestEngine(context);
367 view_ = std::make_unique<TestFlutterWindowsView>(
368 engine_.get(),
369 // The WindowBindingHandler is used for window size and such, and
370 // doesn't affect keyboard.
371 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>(),
372 [this](KeyCall key_call) { key_calls.push_back(key_call); });
373
374 EngineModifier modifier{engine_.get()};
375 modifier.SetImplicitView(view_.get());
376 modifier.InitializeKeyboard();
377
378 window_ = std::make_unique<MockKeyboardManagerDelegate>(
379 view_.get(), [this](UINT virtual_key) -> SHORT {
380 return map_virtual_key_layout_(virtual_key, MAPVK_VK_TO_CHAR);
381 });
382 }
383
384 TestFlutterWindowsView& GetView() { return *view_; }
385 MockKeyboardManagerDelegate& GetWindow() { return *window_; }
386
387 // Reset the keyboard by invoking the engine restart handler.
388 void ResetKeyboard() { EngineModifier{engine_.get()}.Restart(); }
389
390 // Set all events to be handled (true) or unhandled (false).
391 void Responding(bool response) { callback_handler_ = RespondValue(response); }
392
393 // Manually handle event callback of the onKeyData embedder API.
394 //
395 // On every onKeyData call, the |handler| will be invoked with the target
396 // key data and the result callback. Immediately calling the callback with
397 // a boolean is equivalent to setting |Responding| with the boolean. However,
398 // |LateResponding| allows storing the callback to call later.
399 void LateResponding(
401 callback_handler_ = std::move(handler);
402 }
403
404 void SetLayout(MapVirtualKeyLayout layout) {
405 map_virtual_key_layout_ = layout == nullptr ? LayoutDefault : layout;
406 }
407
408 void InjectKeyboardChanges(std::vector<KeyboardChange> changes) {
409 FML_DCHECK(window_ != nullptr);
410 window_->InjectKeyboardChanges(std::move(changes));
411 }
412
413 // Simulates receiving a platform message from the framework.
414 void InjectPlatformMessage(const char* channel,
415 const char* method,
416 const char* args) {
417 rapidjson::Document args_doc;
418 args_doc.Parse(args);
419 FML_DCHECK(!args_doc.HasParseError());
420
421 rapidjson::Document message_doc(rapidjson::kObjectType);
422 auto& allocator = message_doc.GetAllocator();
423 message_doc.AddMember("method", rapidjson::Value(method, allocator),
424 allocator);
425 message_doc.AddMember("args", args_doc, allocator);
426
427 rapidjson::StringBuffer buffer;
428 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
429 message_doc.Accept(writer);
430
431 std::unique_ptr<std::vector<uint8_t>> data =
435 sizeof(FlutterPlatformMessage), // struct_size
436 channel, // channel
437 data->data(), // message
438 data->size(), // message_size
439 &response_handle, // response_handle
440 };
441 view_->GetEngine()->HandlePlatformMessage(&message);
442 }
443
444 // Get the number of redispatched messages since the last clear, then clear
445 // the counter.
446 size_t RedispatchedMessageCountAndClear() {
447 auto& messages = window_->RedispatchedMessages();
448 size_t count = messages.size();
449 messages.clear();
450 return count;
451 }
452
453 void clear_key_calls() {
454 for (KeyCall& key_call : key_calls) {
455 if (key_call.type == KeyCall::kKeyCallOnKey &&
456 key_call.key_event.character != nullptr) {
457 delete[] key_call.key_event.character;
458 }
459 }
460 key_calls.clear();
461 }
462
463 std::vector<KeyCall> key_calls;
464
465 private:
466 std::unique_ptr<FlutterWindowsEngine> engine_;
467 std::unique_ptr<TestFlutterWindowsView> view_;
468 std::unique_ptr<MockKeyboardManagerDelegate> window_;
470 MapVirtualKeyLayout map_virtual_key_layout_;
471
472 // Returns an engine instance configured with dummy project path values, and
473 // overridden methods for sending platform messages, so that the engine can
474 // respond as if the framework were connected.
475 std::unique_ptr<FlutterWindowsEngine> GetTestEngine(
476 WindowsTestContext& context) {
477 FlutterWindowsEngineBuilder builder{context};
478
479 builder.SetCreateKeyboardHandlerCallbacks(
480 [this](int virtual_key) -> SHORT {
481 // `window_` is not initialized yet when this callback is first
482 // called.
483 return window_ ? window_->GetKeyState(virtual_key) : 0;
484 },
485 [this](UINT virtual_key, bool extended) -> SHORT {
486 return map_virtual_key_layout_(
487 virtual_key, extended ? MAPVK_VK_TO_VSC_EX : MAPVK_VK_TO_VSC);
488 });
489
490 auto engine = builder.Build();
491
492 EngineModifier modifier(engine.get());
493
494 auto key_response_controller =
495 std::make_shared<MockKeyResponseController>();
496 key_response_controller->SetEmbedderResponse(
497 [&key_calls = key_calls, &callback_handler = callback_handler_](
498 const FlutterKeyEvent* event,
500 FlutterKeyEvent clone_event = *event;
501 clone_event.character = event->character == nullptr
502 ? nullptr
503 : clone_string(event->character);
504 key_calls.push_back(KeyCall{
505 .type = KeyCall::kKeyCallOnKey,
506 .key_event = clone_event,
507 });
509 });
510 key_response_controller->SetTextInputResponse(
511 [&key_calls =
512 key_calls](std::unique_ptr<rapidjson::Document> document) {
513 rapidjson::StringBuffer buffer;
514 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
515 document->Accept(writer);
516 key_calls.push_back(KeyCall{
517 .type = KeyCall::kKeyCallTextMethodCall,
518 .text_method_call = buffer.GetString(),
519 });
520 });
521 MockEmbedderApiForKeyboard(modifier, key_response_controller);
522
523 engine->Run();
524
525 return engine;
526 }
527
529 bool value) {
530 return [value](const FlutterKeyEvent* event,
532 callback(value);
533 };
534 }
535
536 private:
538};
539
540class KeyboardTest : public WindowsTest {
541 public:
542 KeyboardTest() = default;
543 virtual ~KeyboardTest() = default;
544
545 private:
546 FML_DISALLOW_COPY_AND_ASSIGN(KeyboardTest);
547};
548
549} // namespace
550
551// Define compound `expect` in macros. If they're defined in functions, the
552// stacktrace wouldn't print where the function is called in the unit tests.
553
554#define EXPECT_CALL_IS_EVENT(_key_call, _type, _physical, _logical, \
555 _character, _synthesized) \
556 EXPECT_EQ(_key_call.type, KeyCall::kKeyCallOnKey); \
557 EXPECT_EVENT_EQUALS(_key_call.key_event, _type, _physical, _logical, \
558 _character, _synthesized);
559
560#define EXPECT_CALL_IS_TEXT(_key_call, u16_string) \
561 EXPECT_EQ(_key_call.type, KeyCall::kKeyCallOnText); \
562 EXPECT_EQ(_key_call.text, u16_string);
563
564#define EXPECT_CALL_IS_TEXT_METHOD_CALL(_key_call, json_string) \
565 EXPECT_EQ(_key_call.type, KeyCall::kKeyCallTextMethodCall); \
566 EXPECT_STREQ(_key_call.text_method_call.c_str(), json_string);
567
568TEST_F(KeyboardTest, LowerCaseAHandled) {
569 KeyboardTester tester{GetContext()};
570 tester.Responding(true);
571
572 // US Keyboard layout
573
574 // Press A
575 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
579 kWmResultZero)});
580
581 EXPECT_EQ(tester.key_calls.size(), 1);
583 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
584 tester.clear_key_calls();
585 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
586
587 // Release A
588 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
590 kWmResultZero)});
591
592 EXPECT_EQ(tester.key_calls.size(), 1);
593 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
594 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
595 tester.clear_key_calls();
596 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
597}
598
599TEST_F(KeyboardTest, LowerCaseAUnhandled) {
600 KeyboardTester tester{GetContext()};
601 tester.Responding(false);
602
603 // US Keyboard layout
604
605 // Press A
606 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
610 kWmResultZero)});
611
612 EXPECT_EQ(tester.key_calls.size(), 2);
614 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
615 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"a");
616 tester.clear_key_calls();
617 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
618
619 // Release A
620 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
622 kWmResultZero)});
623
624 EXPECT_EQ(tester.key_calls.size(), 1);
625 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
626 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
627 tester.clear_key_calls();
628 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
629}
630
631TEST_F(KeyboardTest, ArrowLeftHandled) {
632 KeyboardTester tester{GetContext()};
633 tester.Responding(true);
634
635 // US Keyboard layout
636
637 // Press ArrowLeft
638 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
639 WmKeyDownInfo{VK_LEFT, kScanCodeArrowLeft, kExtended, kWasUp}.Build(
640 kWmResultZero)});
641
642 EXPECT_EQ(tester.key_calls.size(), 1);
644 kPhysicalArrowLeft, kLogicalArrowLeft, "",
645 kNotSynthesized);
646 tester.clear_key_calls();
647 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
648
649 // Release ArrowLeft
650 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
651 WmKeyUpInfo{VK_LEFT, kScanCodeArrowLeft, kExtended}.Build(
652 kWmResultZero)});
653
654 EXPECT_EQ(tester.key_calls.size(), 1);
655 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
656 kPhysicalArrowLeft, kLogicalArrowLeft, "",
657 kNotSynthesized);
658 tester.clear_key_calls();
659 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
660}
661
662TEST_F(KeyboardTest, ArrowLeftUnhandled) {
663 KeyboardTester tester{GetContext()};
664 tester.Responding(false);
665
666 // US Keyboard layout
667
668 // Press ArrowLeft
669 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
670 WmKeyDownInfo{VK_LEFT, kScanCodeArrowLeft, kExtended, kWasUp}.Build(
671 kWmResultZero)});
672
673 EXPECT_EQ(tester.key_calls.size(), 1);
675 kPhysicalArrowLeft, kLogicalArrowLeft, "",
676 kNotSynthesized);
677 tester.clear_key_calls();
678 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
679
680 // Release ArrowLeft
681 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
682 WmKeyUpInfo{VK_LEFT, kScanCodeArrowLeft, kExtended}.Build(
683 kWmResultZero)});
684
685 EXPECT_EQ(tester.key_calls.size(), 1);
686 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
687 kPhysicalArrowLeft, kLogicalArrowLeft, "",
688 kNotSynthesized);
689 tester.clear_key_calls();
690 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
691}
692
693TEST_F(KeyboardTest, ShiftLeftUnhandled) {
694 KeyboardTester tester{GetContext()};
695 tester.Responding(false);
696
697 // US Keyboard layout
698
699 // Press ShiftLeft
700 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
701 KeyStateChange{VK_LSHIFT, true, false},
703 kWmResultZero)});
704
705 EXPECT_EQ(tester.key_calls.size(), 1);
707 kPhysicalShiftLeft, kLogicalShiftLeft, "",
708 kNotSynthesized);
709 tester.clear_key_calls();
710 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
711
712 // Hold ShiftLeft
713 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
715 kWmResultZero)});
716
717 EXPECT_EQ(tester.key_calls.size(), 1);
719 kPhysicalShiftLeft, kLogicalShiftLeft, "",
720 kNotSynthesized);
721 tester.clear_key_calls();
722 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
723
724 // Release ShiftLeft
725 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
726 KeyStateChange{VK_LSHIFT, false, true},
728 kWmResultZero)});
729
730 EXPECT_EQ(tester.key_calls.size(), 1);
731 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
732 kPhysicalShiftLeft, kLogicalShiftLeft, "",
733 kNotSynthesized);
734 tester.clear_key_calls();
735 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
736}
737
738TEST_F(KeyboardTest, ShiftRightUnhandled) {
739 KeyboardTester tester{GetContext()};
740 tester.Responding(false);
741
742 // US Keyboard layout
743
744 // Press ShiftRight
745 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
746 KeyStateChange{VK_RSHIFT, true, false},
748 kWmResultZero)});
749
750 EXPECT_EQ(tester.key_calls.size(), 1);
752 kPhysicalShiftRight, kLogicalShiftRight, "",
753 kNotSynthesized);
754 tester.clear_key_calls();
755 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
756
757 // Release ShiftRight
758 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
759 KeyStateChange{VK_RSHIFT, false, true},
761 kWmResultZero)});
762
763 EXPECT_EQ(tester.key_calls.size(), 1);
764 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
765 kPhysicalShiftRight, kLogicalShiftRight, "",
766 kNotSynthesized);
767 tester.clear_key_calls();
768 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
769}
770
771TEST_F(KeyboardTest, CtrlLeftUnhandled) {
772 KeyboardTester tester{GetContext()};
773 tester.Responding(false);
774
775 // US Keyboard layout
776
777 // Press CtrlLeft
778 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
779 KeyStateChange{VK_LCONTROL, true, false},
780 WmKeyDownInfo{VK_CONTROL, kScanCodeControl, kNotExtended, kWasUp}.Build(
781 kWmResultZero)});
782
783 EXPECT_EQ(tester.key_calls.size(), 1);
785 kPhysicalControlLeft, kLogicalControlLeft, "",
786 kNotSynthesized);
787 tester.clear_key_calls();
788 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
789
790 // Release CtrlLeft
791 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
792 KeyStateChange{VK_LCONTROL, false, true},
793 WmKeyUpInfo{VK_SHIFT, kScanCodeControl, kNotExtended}.Build(
794 kWmResultZero)});
795
796 EXPECT_EQ(tester.key_calls.size(), 1);
797 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
798 kPhysicalControlLeft, kLogicalControlLeft, "",
799 kNotSynthesized);
800 tester.clear_key_calls();
801 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
802}
803
804TEST_F(KeyboardTest, CtrlRightUnhandled) {
805 KeyboardTester tester{GetContext()};
806 tester.Responding(false);
807
808 // US Keyboard layout
809
810 // Press CtrlRight
811 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
812 KeyStateChange{VK_RCONTROL, true, false},
813 WmKeyDownInfo{VK_CONTROL, kScanCodeControl, kExtended, kWasUp}.Build(
814 kWmResultZero)});
815
816 EXPECT_EQ(tester.key_calls.size(), 1);
818 kPhysicalControlRight, kLogicalControlRight, "",
819 kNotSynthesized);
820 tester.clear_key_calls();
821 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
822
823 // Release CtrlRight
824 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
825 KeyStateChange{VK_RCONTROL, false, true},
826 WmKeyUpInfo{VK_CONTROL, kScanCodeControl, kExtended}.Build(
827 kWmResultZero)});
828
829 EXPECT_EQ(tester.key_calls.size(), 1);
830 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
831 kPhysicalControlRight, kLogicalControlRight, "",
832 kNotSynthesized);
833 tester.clear_key_calls();
834 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
835}
836
837TEST_F(KeyboardTest, AltLeftUnhandled) {
838 KeyboardTester tester{GetContext()};
839 tester.Responding(false);
840
841 // US Keyboard layout
842
843 // Press AltLeft. AltLeft is a SysKeyDown event.
844 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
845 KeyStateChange{VK_LMENU, true, false},
846 WmSysKeyDownInfo{VK_MENU, kScanCodeAlt, kNotExtended, kWasUp}.Build(
847 kWmResultDefault)}); // Always pass to the default WndProc.
848
849 EXPECT_EQ(tester.key_calls.size(), 1);
851 kPhysicalAltLeft, kLogicalAltLeft, "", kNotSynthesized);
852 tester.clear_key_calls();
853 // Don't redispatch sys messages.
854 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
855
856 // Release AltLeft. AltLeft is a SysKeyUp event.
857 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
858 KeyStateChange{VK_LMENU, false, true},
859 WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kNotExtended}.Build(
860 kWmResultDefault)}); // Always pass to the default WndProc.
861
862 EXPECT_EQ(tester.key_calls.size(), 1);
863 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
864 kPhysicalAltLeft, kLogicalAltLeft, "", kNotSynthesized);
865 tester.clear_key_calls();
866 // Don't redispatch sys messages.
867 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
868}
869
870TEST_F(KeyboardTest, AltRightUnhandled) {
871 KeyboardTester tester{GetContext()};
872 tester.Responding(false);
873
874 // US Keyboard layout
875
876 // Press AltRight. AltRight is a SysKeyDown event.
877 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
878 KeyStateChange{VK_RMENU, true, false},
879 WmSysKeyDownInfo{VK_MENU, kScanCodeAlt, kExtended, kWasUp}.Build(
880 kWmResultDefault)}); // Always pass to the default WndProc.
881
882 EXPECT_EQ(tester.key_calls.size(), 1);
884 kPhysicalAltRight, kLogicalAltRight, "",
885 kNotSynthesized);
886 tester.clear_key_calls();
887 // Don't redispatch sys messages.
888 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
889
890 // Release AltRight. AltRight is a SysKeyUp event.
891 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
892 KeyStateChange{VK_RMENU, false, true},
893 WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kExtended}.Build(
894 kWmResultDefault)}); // Always pass to the default WndProc.
895
896 EXPECT_EQ(tester.key_calls.size(), 1);
897 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
898 kPhysicalAltRight, kLogicalAltRight, "",
899 kNotSynthesized);
900 tester.clear_key_calls();
901 // Don't redispatch sys messages.
902 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
903}
904
905TEST_F(KeyboardTest, MetaLeftUnhandled) {
906 KeyboardTester tester{GetContext()};
907 tester.Responding(false);
908
909 // US Keyboard layout
910
911 // Press MetaLeft
912 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
913 KeyStateChange{VK_LWIN, true, false},
914 WmKeyDownInfo{VK_LWIN, kScanCodeMetaLeft, kExtended, kWasUp}.Build(
915 kWmResultZero)});
916
917 EXPECT_EQ(tester.key_calls.size(), 1);
919 kPhysicalMetaLeft, kLogicalMetaLeft, "",
920 kNotSynthesized);
921 tester.clear_key_calls();
922 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
923
924 // Release MetaLeft
925 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
926 KeyStateChange{VK_LWIN, false, true},
927 WmKeyUpInfo{VK_LWIN, kScanCodeMetaLeft, kExtended}.Build(kWmResultZero)});
928
929 EXPECT_EQ(tester.key_calls.size(), 1);
930 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
931 kPhysicalMetaLeft, kLogicalMetaLeft, "",
932 kNotSynthesized);
933 tester.clear_key_calls();
934 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
935}
936
937TEST_F(KeyboardTest, MetaRightUnhandled) {
938 KeyboardTester tester{GetContext()};
939 tester.Responding(false);
940
941 // US Keyboard layout
942
943 // Press MetaRight
944 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
945 KeyStateChange{VK_RWIN, true, false},
946 WmKeyDownInfo{VK_RWIN, kScanCodeMetaRight, kExtended, kWasUp}.Build(
947 kWmResultZero)});
948
949 EXPECT_EQ(tester.key_calls.size(), 1);
951 kPhysicalMetaRight, kLogicalMetaRight, "",
952 kNotSynthesized);
953 tester.clear_key_calls();
954 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
955
956 // Release MetaRight
957 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
958 KeyStateChange{VK_RWIN, false, true},
959 WmKeyUpInfo{VK_RWIN, kScanCodeMetaRight, kExtended}.Build(
960 kWmResultZero)});
961
962 EXPECT_EQ(tester.key_calls.size(), 1);
963 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
964 kPhysicalMetaRight, kLogicalMetaRight, "",
965 kNotSynthesized);
966 tester.clear_key_calls();
967 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
968}
969
970// Press and hold A. This should generate a repeat event.
971TEST_F(KeyboardTest, RepeatA) {
972 KeyboardTester tester{GetContext()};
973 tester.Responding(true);
974
975 // Press A
976 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
980 kWmResultZero)});
981
982 // Hold A
983 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
987 kWmResultZero)});
988
989 EXPECT_EQ(tester.key_calls.size(), 2);
991 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
993 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
994 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
995}
996
997// Press A, hot restart the engine, and hold A.
998// This should reset the keyboard's state and generate
999// two separate key down events.
1000TEST_F(KeyboardTest, RestartClearsKeyboardState) {
1001 KeyboardTester tester{GetContext()};
1002 tester.Responding(true);
1003
1004 // Press A
1005 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1009 kWmResultZero)});
1010
1011 // Reset the keyboard's state.
1012 tester.ResetKeyboard();
1013
1014 // Hold A. Notice the message declares the key is already down, however, the
1015 // the keyboard does not send a repeat event as its state was reset.
1016 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1020 kWmResultZero)});
1021
1022 EXPECT_EQ(tester.key_calls.size(), 2);
1024 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
1026 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
1027 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
1028}
1029
1030// Press Enter, lose focus before the up event arrives, and press Enter again
1031// once focus returns. The second down event should not be dropped.
1032TEST_F(KeyboardTest, FreshKeyDownAfterMissedUpIsDelivered) {
1033 KeyboardTester tester{GetContext()};
1034 tester.Responding(true);
1035
1036 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1037 WmKeyDownInfo{VK_RETURN, kScanCodeEnter, kNotExtended, kWasUp}.Build(
1038 kWmResultZero)});
1039
1040 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1041 WmKeyDownInfo{VK_RETURN, kScanCodeEnter, kNotExtended, kWasUp}.Build(
1042 kWmResultZero)});
1043
1044 ASSERT_EQ(tester.key_calls.size(), 3u);
1046 kPhysicalEnter, kLogicalEnter, "", kNotSynthesized);
1047 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
1048 kPhysicalEnter, kLogicalEnter, "", kSynthesized);
1050 kPhysicalEnter, kLogicalEnter, "", kNotSynthesized);
1051 tester.clear_key_calls();
1052
1053 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1054 WmKeyUpInfo{VK_RETURN, kScanCodeEnter, kNotExtended}.Build(
1055 kWmResultZero)});
1056
1057 ASSERT_EQ(tester.key_calls.size(), 1u);
1058 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1059 kPhysicalEnter, kLogicalEnter, "", kNotSynthesized);
1060 tester.clear_key_calls();
1061 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0u);
1062}
1063
1064// Press Shift-A. This is special because Win32 gives 'A' as character for the
1065// KeyA press.
1066TEST_F(KeyboardTest, ShiftLeftKeyA) {
1067 KeyboardTester tester{GetContext()};
1068 tester.Responding(false);
1069
1070 // US Keyboard layout
1071
1072 // Press ShiftLeft
1073 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1074 KeyStateChange{VK_LSHIFT, true, true},
1076 kWmResultZero)});
1077
1078 EXPECT_EQ(tester.key_calls.size(), 1);
1080 kPhysicalShiftLeft, kLogicalShiftLeft, "",
1081 kNotSynthesized);
1082 tester.clear_key_calls();
1083 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1084
1085 // Press A
1086 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1090 kWmResultZero)});
1091
1092 EXPECT_EQ(tester.key_calls.size(), 2);
1094 kPhysicalKeyA, kLogicalKeyA, "A", kNotSynthesized);
1095 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"A");
1096 tester.clear_key_calls();
1097 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1098
1099 // Release ShiftLeft
1100 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1101 KeyStateChange{VK_LSHIFT, false, true},
1103 kWmResultZero)});
1104
1105 EXPECT_EQ(tester.key_calls.size(), 1);
1106 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1107 kPhysicalShiftLeft, kLogicalShiftLeft, "",
1108 kNotSynthesized);
1109 tester.clear_key_calls();
1110 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1111
1112 // Release A
1113 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1115 kWmResultZero)});
1116
1117 EXPECT_EQ(tester.key_calls.size(), 1);
1118 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1119 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
1120 tester.clear_key_calls();
1121 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1122}
1123
1124// Press Ctrl-A. This is special because Win32 gives 0x01 as character for the
1125// KeyA press.
1126TEST_F(KeyboardTest, CtrlLeftKeyA) {
1127 KeyboardTester tester{GetContext()};
1128 tester.Responding(false);
1129
1130 // US Keyboard layout
1131
1132 // Press ControlLeft
1133 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1134 KeyStateChange{VK_LCONTROL, true, true},
1135 WmKeyDownInfo{VK_CONTROL, kScanCodeControl, kNotExtended, kWasUp}.Build(
1136 kWmResultZero)});
1137
1138 EXPECT_EQ(tester.key_calls.size(), 1);
1140 kPhysicalControlLeft, kLogicalControlLeft, "",
1141 kNotSynthesized);
1142 tester.clear_key_calls();
1143 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1144
1145 // Press A
1146 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1150 kWmResultZero)});
1151
1152 EXPECT_EQ(tester.key_calls.size(), 1);
1154 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
1155 tester.clear_key_calls();
1156 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1157
1158 // Release A
1159 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1161 kWmResultZero)});
1162
1163 EXPECT_EQ(tester.key_calls.size(), 1);
1164 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1165 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
1166 tester.clear_key_calls();
1167 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1168
1169 // Release ControlLeft
1170 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1171 KeyStateChange{VK_LCONTROL, false, true},
1172 WmKeyUpInfo{VK_CONTROL, kScanCodeControl, kNotExtended}.Build(
1173 kWmResultZero)});
1174
1175 EXPECT_EQ(tester.key_calls.size(), 1);
1176 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1177 kPhysicalControlLeft, kLogicalControlLeft, "",
1178 kNotSynthesized);
1179 tester.clear_key_calls();
1180 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1181}
1182
1183// Press Ctrl-1. This is special because it yields no WM_CHAR for the 1.
1184TEST_F(KeyboardTest, CtrlLeftDigit1) {
1185 KeyboardTester tester{GetContext()};
1186 tester.Responding(false);
1187
1188 // US Keyboard layout
1189
1190 // Press ControlLeft
1191 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1192 KeyStateChange{VK_LCONTROL, true, true},
1193 WmKeyDownInfo{VK_CONTROL, kScanCodeControl, kNotExtended, kWasUp}.Build(
1194 kWmResultZero)});
1195
1196 EXPECT_EQ(tester.key_calls.size(), 1);
1198 kPhysicalControlLeft, kLogicalControlLeft, "",
1199 kNotSynthesized);
1200 tester.clear_key_calls();
1201 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1202
1203 // Press 1
1204 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1205 WmKeyDownInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended, kWasUp}
1206 .Build(kWmResultZero)});
1207
1208 EXPECT_EQ(tester.key_calls.size(), 1);
1210 kPhysicalDigit1, kLogicalDigit1, "", kNotSynthesized);
1211 tester.clear_key_calls();
1212 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1213
1214 // Release 1
1215 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1216 WmKeyUpInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended}.Build(
1217 kWmResultZero)});
1218
1219 EXPECT_EQ(tester.key_calls.size(), 1);
1220 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1221 kPhysicalDigit1, kLogicalDigit1, "", kNotSynthesized);
1222 tester.clear_key_calls();
1223 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1224
1225 // Release ControlLeft
1226 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1227 KeyStateChange{VK_LCONTROL, false, true},
1228 WmKeyUpInfo{VK_CONTROL, kScanCodeControl, kNotExtended}.Build(
1229 kWmResultZero)});
1230
1231 EXPECT_EQ(tester.key_calls.size(), 1);
1232 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1233 kPhysicalControlLeft, kLogicalControlLeft, "",
1234 kNotSynthesized);
1235 tester.clear_key_calls();
1236 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1237}
1238
1239// Press 1 on a French keyboard. This is special because it yields WM_CHAR
1240// with char_code '&'.
1241TEST_F(KeyboardTest, Digit1OnFrenchLayout) {
1242 KeyboardTester tester{GetContext()};
1243 tester.Responding(false);
1244
1245 tester.SetLayout(LayoutFrench);
1246
1247 // Press 1
1248 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1249 WmKeyDownInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended, kWasUp}
1251 WmCharInfo{'&', kScanCodeDigit1, kNotExtended, kWasUp}.Build(
1252 kWmResultZero)});
1253
1254 EXPECT_EQ(tester.key_calls.size(), 2);
1256 kPhysicalDigit1, kLogicalDigit1, "&", kNotSynthesized);
1257 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"&");
1258 tester.clear_key_calls();
1259 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1260
1261 // Release 1
1262 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1263 WmKeyUpInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended}.Build(
1264 kWmResultZero)});
1265
1266 EXPECT_EQ(tester.key_calls.size(), 1);
1267 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1268 kPhysicalDigit1, kLogicalDigit1, "", kNotSynthesized);
1269 tester.clear_key_calls();
1270 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1271}
1272
1273// This tests AltGr-Q on a German keyboard, which should print '@'.
1274TEST_F(KeyboardTest, AltGrModifiedKey) {
1275 KeyboardTester tester{GetContext()};
1276 tester.Responding(false);
1277
1278 // German Keyboard layout
1279
1280 // Press AltGr, which Win32 precedes with a ContrlLeft down.
1281 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1282 KeyStateChange{VK_LCONTROL, true, true},
1283 WmKeyDownInfo{VK_LCONTROL, kScanCodeControl, kNotExtended, kWasUp}.Build(
1285 KeyStateChange{VK_RMENU, true, true},
1286 WmKeyDownInfo{VK_MENU, kScanCodeAlt, kExtended, kWasUp}.Build(
1287 kWmResultZero)});
1288
1289 EXPECT_EQ(tester.key_calls.size(), 2);
1291 kPhysicalControlLeft, kLogicalControlLeft, "",
1292 kNotSynthesized);
1294 kPhysicalAltRight, kLogicalAltRight, "",
1295 kNotSynthesized);
1296 tester.clear_key_calls();
1297 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1298
1299 // Press Q
1300 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1301 WmKeyDownInfo{kVirtualKeyQ, kScanCodeKeyQ, kNotExtended, kWasUp}.Build(
1303 WmCharInfo{'@', kScanCodeKeyQ, kNotExtended, kWasUp}.Build(
1304 kWmResultZero)});
1305
1306 EXPECT_EQ(tester.key_calls.size(), 2);
1308 kPhysicalKeyQ, kLogicalKeyQ, "@", kNotSynthesized);
1309 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"@");
1310 tester.clear_key_calls();
1311 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1312
1313 // Release Q
1314 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1315 WmKeyUpInfo{kVirtualKeyQ, kScanCodeKeyQ, kNotExtended}.Build(
1316 kWmResultZero)});
1317
1318 EXPECT_EQ(tester.key_calls.size(), 1);
1319 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1320 kPhysicalKeyQ, kLogicalKeyQ, "", kNotSynthesized);
1321 tester.clear_key_calls();
1322 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1323
1324 // Release AltGr. Win32 doesn't dispatch ControlLeft up. Instead Flutter will
1325 // forge one. The AltGr is a system key, therefore will be handled by Win32's
1326 // default WndProc.
1327 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1328 KeyStateChange{VK_LCONTROL, false, true},
1329 ExpectForgedMessage{
1330 WmKeyUpInfo{VK_CONTROL, kScanCodeControl, kNotExtended}.Build(
1331 kWmResultZero)},
1332 KeyStateChange{VK_RMENU, false, true},
1333 WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kExtended}.Build(
1335
1336 EXPECT_EQ(tester.key_calls.size(), 2);
1337 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1338 kPhysicalControlLeft, kLogicalControlLeft, "",
1339 kNotSynthesized);
1340 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
1341 kPhysicalAltRight, kLogicalAltRight, "",
1342 kNotSynthesized);
1343 tester.clear_key_calls();
1344 // The sys key up must not be redispatched. The forged ControlLeft up will.
1345 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1346}
1347
1348// Test the following two key sequences at the same time:
1349//
1350// 1. Tap AltGr, then tap AltGr.
1351// 2. Tap AltGr, hold CtrlLeft, tap AltGr, release CtrlLeft.
1352//
1353// The two sequences are indistinguishable until the very end when a CtrlLeft
1354// up event might or might not follow.
1355//
1356// Sequence 1: CtrlLeft down, AltRight down, AltRight up
1357// Sequence 2: CtrlLeft down, AltRight down, AltRight up, CtrlLeft up
1358//
1359// This is because pressing AltGr alone causes Win32 to send a fake "CtrlLeft
1360// down" event first (see |IsKeyDownAltRight| for detailed explanation).
1361TEST_F(KeyboardTest, AltGrTwice) {
1362 KeyboardTester tester{GetContext()};
1363 tester.Responding(false);
1364
1365 // 1. AltGr down.
1366
1367 // The key down event causes a ControlLeft down and a AltRight (extended
1368 // AltLeft) down.
1369 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1370 KeyStateChange{VK_LCONTROL, true, true},
1371 WmKeyDownInfo{VK_CONTROL, kScanCodeControl, kNotExtended, kWasUp}.Build(
1373 KeyStateChange{VK_RMENU, true, true},
1374 WmKeyDownInfo{VK_MENU, kScanCodeAlt, kExtended, kWasUp}.Build(
1375 kWmResultZero)});
1376
1377 EXPECT_EQ(tester.key_calls.size(), 2);
1379 kPhysicalControlLeft, kLogicalControlLeft, "",
1380 kNotSynthesized);
1382 kPhysicalAltRight, kLogicalAltRight, "",
1383 kNotSynthesized);
1384 tester.clear_key_calls();
1385 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1386
1387 // 2. AltGr up.
1388
1389 // The key up event only causes a AltRight (extended AltLeft) up.
1390 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1391 KeyStateChange{VK_LCONTROL, false, true},
1392 ExpectForgedMessage{
1393 WmKeyUpInfo{VK_CONTROL, kScanCodeControl, kNotExtended}.Build(
1394 kWmResultZero)},
1395 KeyStateChange{VK_RMENU, false, true},
1396 WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kExtended}.Build(
1398 EXPECT_EQ(tester.key_calls.size(), 2);
1399 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1400 kPhysicalControlLeft, kLogicalControlLeft, "",
1401 kNotSynthesized);
1402 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
1403 kPhysicalAltRight, kLogicalAltRight, "",
1404 kNotSynthesized);
1405 tester.clear_key_calls();
1406 // The sys key up must not be redispatched. The forged ControlLeft up will.
1407 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1408
1409 // 3. AltGr down (or: ControlLeft down then AltRight down.)
1410
1411 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1412 KeyStateChange{VK_LCONTROL, true, false},
1413 WmKeyDownInfo{VK_CONTROL, kScanCodeControl, kNotExtended, kWasUp}.Build(
1415 KeyStateChange{VK_RMENU, true, true},
1416 WmKeyDownInfo{VK_MENU, kScanCodeAlt, kExtended, kWasUp}.Build(
1417 kWmResultZero)});
1418
1419 EXPECT_EQ(tester.key_calls.size(), 2);
1421 kPhysicalControlLeft, kLogicalControlLeft, "",
1422 kNotSynthesized);
1424 kPhysicalAltRight, kLogicalAltRight, "",
1425 kNotSynthesized);
1426 tester.clear_key_calls();
1427 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1428
1429 // 4. AltGr up.
1430
1431 // The key up event only causes a AltRight (extended AltLeft) up.
1432 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1433 KeyStateChange{VK_LCONTROL, false, false},
1434 ExpectForgedMessage{
1435 WmKeyUpInfo{VK_CONTROL, kScanCodeControl, kNotExtended}.Build(
1436 kWmResultZero)},
1437 KeyStateChange{VK_RMENU, false, false},
1438 WmSysKeyUpInfo{VK_MENU, kScanCodeAlt, kExtended}.Build(
1440 EXPECT_EQ(tester.key_calls.size(), 2);
1441 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1442 kPhysicalControlLeft, kLogicalControlLeft, "",
1443 kNotSynthesized);
1444 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
1445 kPhysicalAltRight, kLogicalAltRight, "",
1446 kNotSynthesized);
1447 tester.clear_key_calls();
1448 // The sys key up must not be redispatched. The forged ControlLeft up will.
1449 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1450
1451 // 5. For key sequence 2: a real ControlLeft up.
1452 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1453 WmKeyUpInfo{VK_CONTROL, kScanCodeControl, kNotExtended}.Build(
1454 kWmResultZero)});
1455 EXPECT_EQ(tester.key_calls.size(), 1);
1456 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeDown, 0, 0, "",
1457 kNotSynthesized);
1458 tester.clear_key_calls();
1459 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
1460}
1461
1462// This tests dead key ^ then E on a French keyboard, which should be combined
1463// into ê.
1464TEST_F(KeyboardTest, DeadKeyThatCombines) {
1465 KeyboardTester tester{GetContext()};
1466 tester.Responding(false);
1467
1468 tester.SetLayout(LayoutFrench);
1469
1470 // Press ^¨ (US: Left bracket)
1471 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1472 WmKeyDownInfo{0xDD, kScanCodeBracketLeft, kNotExtended, kWasUp}.Build(
1474 WmDeadCharInfo{'^', kScanCodeBracketLeft, kNotExtended, kWasUp}.Build(
1475 kWmResultZero)});
1476
1477 EXPECT_EQ(tester.key_calls.size(), 1);
1479 kPhysicalBracketLeft, kLogicalBracketRight, "^",
1480 kNotSynthesized);
1481 tester.clear_key_calls();
1482 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1483
1484 // Release ^¨
1485 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1486 WmKeyUpInfo{0xDD, kScanCodeBracketLeft, kNotExtended}.Build(
1487 kWmResultZero)});
1488
1489 EXPECT_EQ(tester.key_calls.size(), 1);
1490 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1491 kPhysicalBracketLeft, kLogicalBracketRight, "",
1492 kNotSynthesized);
1493 tester.clear_key_calls();
1494 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1495
1496 // Press E
1497 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1498 WmKeyDownInfo{kVirtualKeyE, kScanCodeKeyE, kNotExtended, kWasUp}.Build(
1500 WmCharInfo{0xEA, kScanCodeKeyE, kNotExtended, kWasUp}.Build(
1501 kWmResultZero)});
1502
1503 EXPECT_EQ(tester.key_calls.size(), 2);
1505 kPhysicalKeyE, kLogicalKeyE, "ê", kNotSynthesized);
1506 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"ê");
1507 tester.clear_key_calls();
1508 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1509
1510 // Release E
1511 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1512 WmKeyUpInfo{kVirtualKeyE, kScanCodeKeyE, kNotExtended}.Build(
1513 kWmResultZero)});
1514
1515 EXPECT_EQ(tester.key_calls.size(), 1);
1516 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1517 kPhysicalKeyE, kLogicalKeyE, "", kNotSynthesized);
1518 tester.clear_key_calls();
1519 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1520}
1521
1522// This tests dead key ^ then E on a US INTL keyboard, which should be combined
1523// into ê.
1524//
1525// It is different from French AZERTY because the character that the ^ key is
1526// mapped to does not contain the dead key character somehow.
1527TEST_F(KeyboardTest, DeadKeyWithoutDeadMaskThatCombines) {
1528 KeyboardTester tester{GetContext()};
1529 tester.Responding(false);
1530
1531 // Press ShiftLeft
1532 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1533 KeyStateChange{VK_LSHIFT, true, true},
1535 kWmResultZero)});
1536
1537 EXPECT_EQ(tester.key_calls.size(), 1);
1539 kPhysicalShiftLeft, kLogicalShiftLeft, "",
1540 kNotSynthesized);
1541 tester.clear_key_calls();
1542 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1543
1544 // Press 6^
1545 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1546 WmKeyDownInfo{'6', kScanCodeDigit6, kNotExtended, kWasUp}.Build(
1548 WmDeadCharInfo{'^', kScanCodeDigit6, kNotExtended, kWasUp}.Build(
1549 kWmResultZero)});
1550
1551 EXPECT_EQ(tester.key_calls.size(), 1);
1553 kPhysicalDigit6, kLogicalDigit6, "6", kNotSynthesized);
1554 tester.clear_key_calls();
1555 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1556
1557 // Release 6^
1558 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1559 WmKeyUpInfo{'6', kScanCodeDigit6, kNotExtended}.Build(kWmResultZero)});
1560
1561 EXPECT_EQ(tester.key_calls.size(), 1);
1562 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1563 kPhysicalDigit6, kLogicalDigit6, "", kNotSynthesized);
1564 tester.clear_key_calls();
1565 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1566
1567 // Release ShiftLeft
1568 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1569 KeyStateChange{VK_LSHIFT, false, true},
1571 kWmResultZero)});
1572
1573 EXPECT_EQ(tester.key_calls.size(), 1);
1574 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1575 kPhysicalShiftLeft, kLogicalShiftLeft, "",
1576 kNotSynthesized);
1577 tester.clear_key_calls();
1578 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1579
1580 // Press E
1581 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1582 WmKeyDownInfo{kVirtualKeyE, kScanCodeKeyE, kNotExtended, kWasUp}.Build(
1584 WmCharInfo{0xEA, kScanCodeKeyE, kNotExtended, kWasUp}.Build(
1585 kWmResultZero)});
1586
1587 EXPECT_EQ(tester.key_calls.size(), 2);
1589 kPhysicalKeyE, kLogicalKeyE, "ê", kNotSynthesized);
1590 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"ê");
1591 tester.clear_key_calls();
1592 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1593
1594 // Release E
1595 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1596 WmKeyUpInfo{kVirtualKeyE, kScanCodeKeyE, kNotExtended}.Build(
1597 kWmResultZero)});
1598
1599 EXPECT_EQ(tester.key_calls.size(), 1);
1600 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1601 kPhysicalKeyE, kLogicalKeyE, "", kNotSynthesized);
1602 tester.clear_key_calls();
1603 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1604}
1605
1606// This tests dead key ^ then & (US: 1) on a French keyboard, which do not
1607// combine and should output "^&".
1608TEST_F(KeyboardTest, DeadKeyThatDoesNotCombine) {
1609 KeyboardTester tester{GetContext()};
1610 tester.Responding(false);
1611
1612 tester.SetLayout(LayoutFrench);
1613
1614 // Press ^¨ (US: Left bracket)
1615 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1616 WmKeyDownInfo{0xDD, kScanCodeBracketLeft, kNotExtended, kWasUp}.Build(
1618 WmDeadCharInfo{'^', kScanCodeBracketLeft, kNotExtended, kWasUp}.Build(
1619 kWmResultZero)});
1620
1621 EXPECT_EQ(tester.key_calls.size(), 1);
1623 kPhysicalBracketLeft, kLogicalBracketRight, "^",
1624 kNotSynthesized);
1625 tester.clear_key_calls();
1626 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1627
1628 // Release ^¨
1629 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1630 WmKeyUpInfo{0xDD, kScanCodeBracketLeft, kNotExtended}.Build(
1631 kWmResultZero)});
1632
1633 EXPECT_EQ(tester.key_calls.size(), 1);
1634 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1635 kPhysicalBracketLeft, kLogicalBracketRight, "",
1636 kNotSynthesized);
1637 tester.clear_key_calls();
1638 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1639
1640 // Press 1
1641 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1642 WmKeyDownInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended, kWasUp}
1644 WmCharInfo{'^', kScanCodeDigit1, kNotExtended, kWasUp}.Build(
1646 WmCharInfo{'&', kScanCodeDigit1, kNotExtended, kWasUp}.Build(
1647 kWmResultZero)});
1648
1649 EXPECT_EQ(tester.key_calls.size(), 3);
1651 kPhysicalDigit1, kLogicalDigit1, "^", kNotSynthesized);
1652 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"^");
1653 EXPECT_CALL_IS_TEXT(tester.key_calls[2], u"&");
1654 tester.clear_key_calls();
1655 // TODO(dkwingsmt): This count should probably be 3. Currently the '^'
1656 // message is redispatched due to being part of the KeyDown session, which is
1657 // not handled by the framework, while the '&' message is not redispatched
1658 // for being a standalone message. We should resolve this inconsistency.
1659 // https://github.com/flutter/flutter/issues/98306
1660 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1661
1662 // Release 1
1663 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1664 WmKeyUpInfo{kVirtualDigit1, kScanCodeDigit1, kNotExtended}.Build(
1665 kWmResultZero)});
1666
1667 EXPECT_EQ(tester.key_calls.size(), 1);
1668 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1669 kPhysicalDigit1, kLogicalDigit1, "", kNotSynthesized);
1670 tester.clear_key_calls();
1671 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1672}
1673
1674// This tests dead key `, then dead key `, then e.
1675//
1676// It should output ``e, instead of `è.
1677TEST_F(KeyboardTest, DeadKeyTwiceThenLetter) {
1678 KeyboardTester tester{GetContext()};
1679 tester.Responding(false);
1680
1681 // US INTL layout.
1682
1683 // Press `
1684 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1685 WmKeyDownInfo{0xC0, kScanCodeBackquote, kNotExtended, kWasUp}.Build(
1687 WmDeadCharInfo{'`', kScanCodeBackquote, kNotExtended, kWasUp}.Build(
1688 kWmResultZero)});
1689
1690 EXPECT_EQ(tester.key_calls.size(), 1);
1692 kPhysicalBackquote, kLogicalBackquote, "`",
1693 kNotSynthesized);
1694 tester.clear_key_calls();
1695 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1696
1697 // Release `
1698 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1699 WmKeyUpInfo{0xC0, kScanCodeBackquote, kNotExtended}.Build(
1700 kWmResultZero)});
1701
1702 EXPECT_EQ(tester.key_calls.size(), 1);
1703 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1704 kPhysicalBackquote, kLogicalBackquote, "",
1705 kNotSynthesized);
1706 tester.clear_key_calls();
1707 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1708
1709 // Press ` again.
1710 // The response should be slow.
1711 std::vector<MockKeyResponseController::ResponseCallback> recorded_callbacks;
1712 tester.LateResponding(
1713 [&recorded_callbacks](
1714 const FlutterKeyEvent* event,
1716 recorded_callbacks.push_back(callback);
1717 });
1718
1719 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1720 WmKeyDownInfo{0xC0, kScanCodeBackquote, kNotExtended, kWasUp}.Build(
1722 WmCharInfo{'`', kScanCodeBackquote, kNotExtended, kWasUp, kBeingReleased,
1723 kNoContext, 1, /*bit25*/ true}
1725 WmCharInfo{'`', kScanCodeBackquote, kNotExtended, kWasUp}.Build(
1726 kWmResultZero)});
1727
1728 EXPECT_EQ(recorded_callbacks.size(), 1);
1729 EXPECT_EQ(tester.key_calls.size(), 1);
1731 kPhysicalBackquote, kLogicalBackquote, "`",
1732 kNotSynthesized);
1733 tester.clear_key_calls();
1734 // Key down event responded with false.
1735 recorded_callbacks.front()(false);
1736 EXPECT_EQ(tester.key_calls.size(), 2);
1737 EXPECT_CALL_IS_TEXT(tester.key_calls[0], u"`");
1738 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"`");
1739 tester.clear_key_calls();
1740 // TODO(dkwingsmt): This count should probably be 3. See the comment above
1741 // that is marked with the same issue.
1742 // https://github.com/flutter/flutter/issues/98306
1743 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
1744
1745 tester.Responding(false);
1746
1747 // Release `
1748 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1749 WmKeyUpInfo{0xC0, kScanCodeBackquote, kNotExtended}.Build(
1750 kWmResultZero)});
1751
1752 EXPECT_EQ(tester.key_calls.size(), 1);
1753 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1754 kPhysicalBackquote, kLogicalBackquote, "",
1755 kNotSynthesized);
1756 tester.clear_key_calls();
1757 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1758}
1759
1760// This tests when the resulting character needs to be combined with surrogates.
1761TEST_F(KeyboardTest, MultibyteCharacter) {
1762 KeyboardTester tester{GetContext()};
1763 tester.Responding(false);
1764
1765 // Gothic Keyboard layout. (We need a layout that yields non-BMP characters
1766 // without IME, which is actually very rare.)
1767
1768 // Press key W of a US keyboard, which should yield character '𐍅'.
1769 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1770 WmKeyDownInfo{kVirtualKeyW, kScanCodeKeyW, kNotExtended, kWasUp}.Build(
1772 WmCharInfo{0xd800, kScanCodeKeyW, kNotExtended, kWasUp}.Build(
1774 WmCharInfo{0xdf45, kScanCodeKeyW, kNotExtended, kWasUp}.Build(
1775 kWmResultZero)});
1776
1777 const char* st = tester.key_calls[0].key_event.character;
1778
1779 EXPECT_EQ(tester.key_calls.size(), 2);
1781 kPhysicalKeyW, kLogicalKeyW, "𐍅", kNotSynthesized);
1782 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"𐍅");
1783 tester.clear_key_calls();
1784 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 3);
1785
1786 // Release W
1787 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1788 WmKeyUpInfo{kVirtualKeyW, kScanCodeKeyW, kNotExtended}.Build(
1789 kWmResultZero)});
1790
1791 EXPECT_EQ(tester.key_calls.size(), 1);
1792 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1793 kPhysicalKeyW, kLogicalKeyW, "", kNotSynthesized);
1794 tester.clear_key_calls();
1795 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1796}
1797
1798TEST_F(KeyboardTest, SynthesizeModifiers) {
1799 KeyboardTester tester{GetContext()};
1800 tester.Responding(false);
1801
1802 // Two dummy events used to trigger synthesization.
1803 Win32Message event1 =
1804 WmKeyDownInfo{VK_BACK, kScanCodeBackspace, kNotExtended, kWasUp}.Build(
1806 Win32Message event2 =
1807 WmKeyUpInfo{VK_BACK, kScanCodeBackspace, kNotExtended}.Build(
1809
1810 // ShiftLeft
1811 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1812 KeyStateChange{VK_LSHIFT, true, true}, event1});
1813 EXPECT_EQ(tester.key_calls.size(), 2);
1815 kPhysicalShiftLeft, kLogicalShiftLeft, "", kSynthesized);
1816 tester.clear_key_calls();
1817 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1818
1819 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1820 KeyStateChange{VK_LSHIFT, false, true}, event2});
1821 EXPECT_EQ(tester.key_calls.size(), 2);
1822 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1823 kPhysicalShiftLeft, kLogicalShiftLeft, "", kSynthesized);
1824 tester.clear_key_calls();
1825 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1826
1827 // ShiftRight
1828 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1829 KeyStateChange{VK_RSHIFT, true, true}, event1});
1830 EXPECT_EQ(tester.key_calls.size(), 2);
1832 kPhysicalShiftRight, kLogicalShiftRight, "",
1833 kSynthesized);
1834 tester.clear_key_calls();
1835 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1836
1837 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1838 KeyStateChange{VK_RSHIFT, false, true}, event2});
1839 EXPECT_EQ(tester.key_calls.size(), 2);
1840 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1841 kPhysicalShiftRight, kLogicalShiftRight, "",
1842 kSynthesized);
1843 tester.clear_key_calls();
1844 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1845
1846 // ControlLeft
1847 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1848 KeyStateChange{VK_LCONTROL, true, true}, event1});
1849 EXPECT_EQ(tester.key_calls.size(), 2);
1851 kPhysicalControlLeft, kLogicalControlLeft, "",
1852 kSynthesized);
1853 tester.clear_key_calls();
1854 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1855
1856 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1857 KeyStateChange{VK_LCONTROL, false, true}, event2});
1858 EXPECT_EQ(tester.key_calls.size(), 2);
1859 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1860 kPhysicalControlLeft, kLogicalControlLeft, "",
1861 kSynthesized);
1862 tester.clear_key_calls();
1863 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1864
1865 // ControlRight
1866 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1867 KeyStateChange{VK_RCONTROL, true, true}, event1});
1868 EXPECT_EQ(tester.key_calls.size(), 2);
1870 kPhysicalControlRight, kLogicalControlRight, "",
1871 kSynthesized);
1872 tester.clear_key_calls();
1873 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1874
1875 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1876 KeyStateChange{VK_RCONTROL, false, true}, event2});
1877 EXPECT_EQ(tester.key_calls.size(), 2);
1878 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1879 kPhysicalControlRight, kLogicalControlRight, "",
1880 kSynthesized);
1881 tester.clear_key_calls();
1882 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1883
1884 // AltLeft
1885 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1886 KeyStateChange{VK_LMENU, true, true}, event1});
1887 EXPECT_EQ(tester.key_calls.size(), 2);
1889 kPhysicalAltLeft, kLogicalAltLeft, "", kSynthesized);
1890 tester.clear_key_calls();
1891 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1892
1893 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1894 KeyStateChange{VK_LMENU, false, true}, event2});
1895 EXPECT_EQ(tester.key_calls.size(), 2);
1896 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1897 kPhysicalAltLeft, kLogicalAltLeft, "", kSynthesized);
1898 tester.clear_key_calls();
1899 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1900
1901 // AltRight
1902 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1903 KeyStateChange{VK_RMENU, true, true}, event1});
1904 EXPECT_EQ(tester.key_calls.size(), 2);
1906 kPhysicalAltRight, kLogicalAltRight, "", kSynthesized);
1907 tester.clear_key_calls();
1908 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1909
1910 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1911 KeyStateChange{VK_RMENU, false, true}, event2});
1912 EXPECT_EQ(tester.key_calls.size(), 2);
1913 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1914 kPhysicalAltRight, kLogicalAltRight, "", kSynthesized);
1915 tester.clear_key_calls();
1916 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1917
1918 // MetaLeft
1919 tester.InjectKeyboardChanges(
1920 std::vector<KeyboardChange>{KeyStateChange{VK_LWIN, true, true}, event1});
1921 EXPECT_EQ(tester.key_calls.size(), 2);
1923 kPhysicalMetaLeft, kLogicalMetaLeft, "", kSynthesized);
1924 tester.clear_key_calls();
1925 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1926
1927 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1928 KeyStateChange{VK_LWIN, false, true}, event2});
1929 EXPECT_EQ(tester.key_calls.size(), 2);
1930 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1931 kPhysicalMetaLeft, kLogicalMetaLeft, "", kSynthesized);
1932 tester.clear_key_calls();
1933 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1934
1935 // MetaRight
1936 tester.InjectKeyboardChanges(
1937 std::vector<KeyboardChange>{KeyStateChange{VK_RWIN, true, true}, event1});
1938 EXPECT_EQ(tester.key_calls.size(), 2);
1940 kPhysicalMetaRight, kLogicalMetaRight, "", kSynthesized);
1941 tester.clear_key_calls();
1942 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1943
1944 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1945 KeyStateChange{VK_RWIN, false, true}, event2});
1946 EXPECT_EQ(tester.key_calls.size(), 2);
1947 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1948 kPhysicalMetaRight, kLogicalMetaRight, "", kSynthesized);
1949 tester.clear_key_calls();
1950 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1951
1952 // CapsLock, phase 0 -> 2 -> 0.
1953 // (For phases, see |SynchronizeCriticalToggledStates|.)
1954 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1955 KeyStateChange{VK_CAPITAL, false, true}, event1});
1956 EXPECT_EQ(tester.key_calls.size(), 3);
1958 kPhysicalCapsLock, kLogicalCapsLock, "", kSynthesized);
1959 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
1960 kPhysicalCapsLock, kLogicalCapsLock, "", kSynthesized);
1961 tester.clear_key_calls();
1962 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1963
1964 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1965 KeyStateChange{VK_CAPITAL, false, false}, event2});
1966 EXPECT_EQ(tester.key_calls.size(), 3);
1968 kPhysicalCapsLock, kLogicalCapsLock, "", kSynthesized);
1969 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
1970 kPhysicalCapsLock, kLogicalCapsLock, "", kSynthesized);
1971 tester.clear_key_calls();
1972 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1973
1974 // ScrollLock, phase 0 -> 1 -> 3
1975 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1976 KeyStateChange{VK_SCROLL, true, true}, event1});
1977 EXPECT_EQ(tester.key_calls.size(), 2);
1979 kPhysicalScrollLock, kLogicalScrollLock, "",
1980 kSynthesized);
1981 tester.clear_key_calls();
1982 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1983
1984 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1985 KeyStateChange{VK_SCROLL, true, false}, event2});
1986 EXPECT_EQ(tester.key_calls.size(), 3);
1987 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1988 kPhysicalScrollLock, kLogicalScrollLock, "",
1989 kSynthesized);
1991 kPhysicalScrollLock, kLogicalScrollLock, "",
1992 kSynthesized);
1993 tester.clear_key_calls();
1994 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
1995
1996 // NumLock, phase 0 -> 3 -> 2
1997 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1998 KeyStateChange{VK_NUMLOCK, true, false}, event1});
1999 // TODO(dkwingsmt): Synthesizing from phase 0 to 3 should yield a full key
2000 // tap and a key down. Fix the algorithm so that the following result becomes
2001 // 4 keycalls with an extra pair of key down and up.
2002 // https://github.com/flutter/flutter/issues/98533
2003 EXPECT_EQ(tester.key_calls.size(), 2);
2005 kPhysicalNumLock, kLogicalNumLock, "", kSynthesized);
2006 tester.clear_key_calls();
2007 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
2008
2009 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2010 KeyStateChange{VK_NUMLOCK, false, true}, event2});
2011 EXPECT_EQ(tester.key_calls.size(), 4);
2012 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
2013 kPhysicalNumLock, kLogicalNumLock, "", kSynthesized);
2015 kPhysicalNumLock, kLogicalNumLock, "", kSynthesized);
2016 EXPECT_CALL_IS_EVENT(tester.key_calls[2], kFlutterKeyEventTypeUp,
2017 kPhysicalNumLock, kLogicalNumLock, "", kSynthesized);
2018 tester.clear_key_calls();
2019 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
2020}
2021
2022// Pressing extended keys during IME events should work properly by not sending
2023// any events.
2024//
2025// Regression test for https://github.com/flutter/flutter/issues/95888 .
2026TEST_F(KeyboardTest, ImeExtendedEventsAreIgnored) {
2027 KeyboardTester tester{GetContext()};
2028 tester.Responding(false);
2029
2030 // US Keyboard layout.
2031
2032 // There should be preceding key events to make the keyboard into IME mode.
2033 // Omit them in this test since they are not relavent.
2034
2035 // Press CtrlRight in IME mode.
2036 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2037 KeyStateChange{VK_RCONTROL, true, false},
2038 WmKeyDownInfo{VK_PROCESSKEY, kScanCodeControl, kExtended, kWasUp}.Build(
2039 kWmResultZero)});
2040
2041 EXPECT_EQ(tester.key_calls.size(), 1);
2042 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeDown, 0, 0, "",
2043 kNotSynthesized);
2044 tester.clear_key_calls();
2045 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2046}
2047
2048// Ensures that synthesization works correctly when a Shift key is pressed and
2049// (only) its up event is labeled as an IME event (VK_PROCESSKEY).
2050//
2051// Regression test for https://github.com/flutter/flutter/issues/104169. These
2052// are real messages recorded when pressing Shift-2 using Microsoft Pinyin IME
2053// on Win 10 Enterprise, which crashed the app before the fix.
2054TEST_F(KeyboardTest, UpOnlyImeEventsAreCorrectlyHandled) {
2055 KeyboardTester tester{GetContext()};
2056 tester.Responding(true);
2057
2058 // US Keyboard layout.
2059
2060 // Press CtrlRight in IME mode.
2061 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2062 KeyStateChange{VK_LSHIFT, true, false},
2065 WmKeyDownInfo{VK_PROCESSKEY, kScanCodeDigit2, kNotExtended, kWasUp}.Build(
2067 KeyStateChange{VK_LSHIFT, false, true},
2070 WmKeyUpInfo{'2', kScanCodeDigit2, kNotExtended, kWasUp}.Build(
2071 kWmResultZero)});
2072
2073 EXPECT_EQ(tester.key_calls.size(), 4);
2075 kPhysicalShiftLeft, kLogicalShiftLeft, "",
2076 kNotSynthesized);
2077 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeDown, 0, 0, "",
2078 kNotSynthesized);
2079 EXPECT_CALL_IS_EVENT(tester.key_calls[2], kFlutterKeyEventTypeUp,
2080 kPhysicalShiftLeft, kLogicalShiftLeft, "",
2081 kNotSynthesized);
2082 EXPECT_CALL_IS_EVENT(tester.key_calls[3], kFlutterKeyEventTypeDown, 0, 0, "",
2083 kNotSynthesized);
2084 tester.clear_key_calls();
2085}
2086
2087// Regression test for a crash in an earlier implementation.
2088//
2089// In real life, the framework responds slowly. The next real event might
2090// arrive earlier than the framework response, and if the 2nd event has an
2091// identical hash as the one waiting for response, an earlier implementation
2092// will crash upon the response.
2093TEST_F(KeyboardTest, SlowFrameworkResponse) {
2094 KeyboardTester tester{GetContext()};
2095
2096 std::vector<MockKeyResponseController::ResponseCallback> recorded_callbacks;
2097
2098 // Store callbacks to manually call them.
2099 tester.LateResponding(
2100 [&recorded_callbacks](
2101 const FlutterKeyEvent* event,
2103 recorded_callbacks.push_back(callback);
2104 });
2105
2106 // Press A
2107 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2111 kWmResultZero)});
2112
2113 // Hold A
2114 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2118 kWmResultZero)});
2119
2120 EXPECT_EQ(tester.key_calls.size(), 1);
2122 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
2123 EXPECT_EQ(recorded_callbacks.size(), 1);
2124 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2125
2126 // The first response.
2127 recorded_callbacks.front()(false);
2128
2129 EXPECT_EQ(tester.key_calls.size(), 3);
2130 EXPECT_EQ(recorded_callbacks.size(), 2);
2131 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"a");
2133 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
2134 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
2135
2136 // The second response.
2137 recorded_callbacks.back()(false);
2138
2139 EXPECT_EQ(tester.key_calls.size(), 4);
2140 EXPECT_CALL_IS_TEXT(tester.key_calls[3], u"a");
2141 tester.clear_key_calls();
2142 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
2143}
2144
2145// Regression test for https://github.com/flutter/flutter/issues/84210.
2146//
2147// When the framework response is slow during a sequence of identical messages,
2148// make sure the real messages are not mistaken as redispatched messages,
2149// in order to not mess up the order of events.
2150//
2151// In this test we use:
2152//
2153// KeyA down, KeyA up, (down event responded with false), KeyA down, KeyA up,
2154//
2155// The code must not take the 2nd real key down events as a redispatched event.
2156TEST_F(KeyboardTest, SlowFrameworkResponseForIdenticalEvents) {
2157 KeyboardTester tester{GetContext()};
2158 std::vector<MockKeyResponseController::ResponseCallback> recorded_callbacks;
2159
2160 // Store callbacks to manually call them.
2161 tester.LateResponding(
2162 [&recorded_callbacks](
2163 const FlutterKeyEvent* event,
2165 recorded_callbacks.push_back(callback);
2166 });
2167
2168 // Press A
2169 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2173 kWmResultZero)});
2174
2175 EXPECT_EQ(tester.key_calls.size(), 1);
2177 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
2178 tester.clear_key_calls();
2179 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2180
2181 // Release A
2182 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2184 kWmResultZero)});
2185
2186 EXPECT_EQ(tester.key_calls.size(), 0);
2187 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2188
2189 // The first down event responded with false.
2190 EXPECT_EQ(recorded_callbacks.size(), 1);
2191 recorded_callbacks.front()(false);
2192
2193 EXPECT_EQ(tester.key_calls.size(), 2);
2194 EXPECT_CALL_IS_TEXT(tester.key_calls[0], u"a");
2195 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
2196 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
2197 tester.clear_key_calls();
2198 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
2199
2200 // Press A again
2201 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2205 kWmResultZero)});
2206
2207 // Nothing more was dispatched because the first up event hasn't been
2208 // responded yet.
2209 EXPECT_EQ(recorded_callbacks.size(), 2);
2210 EXPECT_EQ(tester.key_calls.size(), 0);
2211 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2212
2213 // The first up event responded with false, which was redispatched, and caused
2214 // the down event to be dispatched.
2215 recorded_callbacks.back()(false);
2216 EXPECT_EQ(tester.key_calls.size(), 1);
2218 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
2219 tester.clear_key_calls();
2220 EXPECT_EQ(recorded_callbacks.size(), 3);
2221 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
2222
2223 // Release A again
2224 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2226 kWmResultZero)});
2227
2228 EXPECT_EQ(tester.key_calls.size(), 0);
2229 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2230}
2231
2232TEST_F(KeyboardTest, TextInputSubmit) {
2233 KeyboardTester tester{GetContext()};
2234 tester.Responding(false);
2235
2236 // US Keyboard layout
2237
2238 tester.InjectPlatformMessage(
2239 "flutter/textinput", "TextInput.setClient",
2240 R"|([108, {"inputAction": "TextInputAction.none", "viewId": 0}])|");
2241
2242 // Press Enter
2243 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2244 WmKeyDownInfo{VK_RETURN, kScanCodeEnter, kNotExtended, kWasUp}.Build(
2246 WmCharInfo{'\n', kScanCodeEnter, kNotExtended, kWasUp}.Build(
2247 kWmResultZero)});
2248
2249 EXPECT_EQ(tester.key_calls.size(), 2);
2251 kPhysicalEnter, kLogicalEnter, "", kNotSynthesized);
2253 tester.key_calls[1],
2254 "{"
2255 R"|("method":"TextInputClient.performAction",)|"
2256 R"|("args":[108,"TextInputAction.none"])|"
2257 "}");
2258 tester.clear_key_calls();
2259 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
2260
2261 // Release Enter
2262 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2263 WmKeyUpInfo{VK_RETURN, kScanCodeEnter, kNotExtended}.Build(
2264 kWmResultZero)});
2265
2266 EXPECT_EQ(tester.key_calls.size(), 1);
2267 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
2268 kPhysicalEnter, kLogicalEnter, "", kNotSynthesized);
2269 tester.clear_key_calls();
2270 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
2271
2272 // Make sure OnText is not obstructed after pressing Enter.
2273 //
2274 // Regression test for https://github.com/flutter/flutter/issues/97706.
2275
2276 // Press A
2277 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2281 kWmResultZero)});
2282
2283 EXPECT_EQ(tester.key_calls.size(), 2);
2285 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
2286 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"a");
2287 tester.clear_key_calls();
2288
2289 // Release A
2290 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2292 kWmResultZero)});
2293
2294 EXPECT_EQ(tester.key_calls.size(), 1);
2295 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
2296 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
2297 tester.clear_key_calls();
2298}
2299
2300TEST_F(KeyboardTest, VietnameseTelexAddDiacriticWithFastResponse) {
2301 // In this test, the user presses the folloing keys:
2302 //
2303 // Key Current text
2304 // ===========================
2305 // A a
2306 // F à
2307 //
2308 // And the Backspace event is responded immediately.
2309
2310 KeyboardTester tester{GetContext()};
2311 tester.Responding(false);
2312
2313 // US Keyboard layout
2314
2315 // Press A
2316 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2320 kWmResultZero)});
2321
2322 EXPECT_EQ(tester.key_calls.size(), 2);
2324 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
2325 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"a");
2326 tester.clear_key_calls();
2327 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
2328
2329 // Release A
2330 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2332 kWmResultZero)});
2333
2334 EXPECT_EQ(tester.key_calls.size(), 1);
2335 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
2336 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
2337 tester.clear_key_calls();
2338 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
2339
2340 // Press F, which is translated to:
2341 //
2342 // Backspace down, char & up, then VK_PACKET('à').
2343 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2344 WmKeyDownInfo{VK_BACK, kScanCodeBackspace, kNotExtended, kWasUp}.Build(
2346 WmCharInfo{0x8, kScanCodeBackspace, kNotExtended, kWasUp}.Build(
2348 WmKeyUpInfo{VK_BACK, kScanCodeBackspace, kNotExtended}.Build(
2351 WmCharInfo{0xe0 /*'à'*/, 0, kNotExtended, kWasUp}.Build(kWmResultZero),
2353
2354 EXPECT_EQ(tester.key_calls.size(), 3);
2356 kPhysicalBackspace, kLogicalBackspace, "",
2357 kNotSynthesized);
2358 EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
2359 kPhysicalBackspace, kLogicalBackspace, "",
2360 kNotSynthesized);
2361 EXPECT_CALL_IS_TEXT(tester.key_calls[2], u"à");
2362 tester.clear_key_calls();
2363 // TODO(dkwingsmt): This count should probably be 4. Currently the CHAR 0x8
2364 // message is redispatched due to being part of the KeyDown session, which is
2365 // not handled by the framework, while the 'à' message is not redispatched
2366 // for being a standalone message. We should resolve this inconsistency.
2367 // https://github.com/flutter/flutter/issues/98306
2368 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 3);
2369
2370 // Release F
2371 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2372 WmKeyUpInfo{kVirtualKeyF, kScanCodeKeyF, kNotExtended,
2373 /* overwrite_prev_state_0 */ true}
2374 .Build(kWmResultZero)});
2375
2376 EXPECT_EQ(tester.key_calls.size(), 1);
2377 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeDown, 0, 0, "",
2378 kNotSynthesized);
2379 tester.clear_key_calls();
2380 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2381}
2382
2384 bool backspace_response) {
2385 // In this test, the user presses the following keys:
2386 //
2387 // Key Current text
2388 // ===========================
2389 // A a
2390 // F à
2391 //
2392 // And the Backspace down event is responded slowly with `backspace_response`.
2393
2394 KeyboardTester tester{context};
2395 tester.Responding(false);
2396
2397 // US Keyboard layout
2398
2399 // Press A
2400 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2404 kWmResultZero)});
2405
2406 EXPECT_EQ(tester.key_calls.size(), 2);
2408 kPhysicalKeyA, kLogicalKeyA, "a", kNotSynthesized);
2409 EXPECT_CALL_IS_TEXT(tester.key_calls[1], u"a");
2410 tester.clear_key_calls();
2411 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 2);
2412
2413 // Release A
2414 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2416 kWmResultZero)});
2417
2418 EXPECT_EQ(tester.key_calls.size(), 1);
2419 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
2420 kPhysicalKeyA, kLogicalKeyA, "", kNotSynthesized);
2421 tester.clear_key_calls();
2422 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
2423
2424 std::vector<MockKeyResponseController::ResponseCallback> recorded_callbacks;
2425 tester.LateResponding(
2426 [&recorded_callbacks](
2427 const FlutterKeyEvent* event,
2429 recorded_callbacks.push_back(callback);
2430 });
2431
2432 // Press F, which is translated to:
2433 //
2434 // Backspace down, char & up, VK_PACKET('à').
2435 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2436 WmKeyDownInfo{VK_BACK, kScanCodeBackspace, kNotExtended, kWasUp}.Build(
2438 WmCharInfo{0x8, kScanCodeBackspace, kNotExtended, kWasUp}.Build(
2440 WmKeyUpInfo{VK_BACK, kScanCodeBackspace, kNotExtended}.Build(
2443 WmCharInfo{0xe0 /*'à'*/, 0, kNotExtended, kWasUp}.Build(kWmResultZero),
2445
2446 // The Backspace event has not responded yet, therefore the char message must
2447 // hold. This is because when the framework is handling the Backspace event,
2448 // it will send a setEditingState message that updates the text state that has
2449 // the last character deleted (denoted by `string1`). Processing the char
2450 // message before then will cause the final text to set to `string1`.
2451 EXPECT_EQ(tester.key_calls.size(), 1);
2453 kPhysicalBackspace, kLogicalBackspace, "",
2454 kNotSynthesized);
2455 tester.clear_key_calls();
2456 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2457
2458 EXPECT_EQ(recorded_callbacks.size(), 1);
2459 recorded_callbacks[0](backspace_response);
2460
2461 EXPECT_EQ(tester.key_calls.size(), 1);
2462 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
2463 kPhysicalBackspace, kLogicalBackspace, "",
2464 kNotSynthesized);
2465 tester.clear_key_calls();
2466 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(),
2467 backspace_response ? 0 : 2);
2468
2469 recorded_callbacks[1](false);
2470 EXPECT_EQ(tester.key_calls.size(), 1);
2471 EXPECT_CALL_IS_TEXT(tester.key_calls[0], u"à");
2472 tester.clear_key_calls();
2473 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
2474
2475 tester.Responding(false);
2476
2477 // Release F
2478 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2479 WmKeyUpInfo{kVirtualKeyF, kScanCodeKeyF, kNotExtended,
2480 /* overwrite_prev_state_0 */ true}
2481 .Build(kWmResultZero)});
2482
2483 EXPECT_EQ(tester.key_calls.size(), 1);
2484 EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeDown, 0, 0, "",
2485 kNotSynthesized);
2486 tester.clear_key_calls();
2487 EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
2488}
2489
2490TEST_F(KeyboardTest, VietnameseTelexAddDiacriticWithSlowFalseResponse) {
2492}
2493
2494TEST_F(KeyboardTest, VietnameseTelexAddDiacriticWithSlowTrueResponse) {
2496}
2497
2498// Ensure that the scancode-less key events issued by Narrator
2499// when toggling caps lock don't violate assert statements.
2500TEST_F(KeyboardTest, DoubleCapsLock) {
2501 KeyboardTester tester{GetContext()};
2502 tester.Responding(false);
2503
2504 tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
2505 WmKeyDownInfo{VK_CAPITAL, 0, kNotExtended}.Build(),
2506 WmKeyUpInfo{VK_CAPITAL, 0, kNotExtended}.Build()});
2507
2508 tester.clear_key_calls();
2509}
2510
2511} // namespace testing
2512} // 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:1348
@ kFlutterKeyEventTypeUp
Definition embedder.h:1347
@ kFlutterKeyEventTypeRepeat
Definition embedder.h:1349
GLFWwindow * window
Definition main.cc:60
FlutterEngine engine
Definition main.cc:84
FlView * view
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
const gchar * channel
const gchar FlBinaryMessengerMessageHandler handler
G_BEGIN_DECLS GBytes * message
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
Win32Message expected_forged_message
union flutter::testing::@2827::KeyboardChange::@78 content
#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:1409
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