Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
fl_keyboard_manager_test.cc File Reference

Go to the source code of this file.

Classes

struct  CallRecord
 

Macros

#define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR)
 

Functions

static CallRecordcall_record_new (const FlutterKeyEvent *event, FlutterKeyEventCallback callback, void *callback_user_data)
 
static void call_record_free (CallRecord *record)
 
static void call_record_respond (CallRecord *record, bool handled)
 

Macro Definition Documentation

◆ VERIFY_DOWN

#define VERIFY_DOWN (   OUT_LOGICAL,
  OUT_CHAR 
)
Value:
EXPECT_EQ(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)) \
EXPECT_EQ(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)) \
->event_logical, \
(OUT_LOGICAL)); \
EXPECT_STREQ(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)) \
->event_character, \
(OUT_CHAR)); \
EXPECT_EQ(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)) \
->event_synthesized, \
false); \
g_ptr_array_set_size(call_records, 0)
ax::mojom::Event event_type
@ kFlutterKeyEventTypeDown
Definition embedder.h:1403

Definition at line 20 of file fl_keyboard_manager_test.cc.

34 {
36 uint64_t event_physical;
37 uint64_t event_logical;
38 gchar* event_character;
39 bool event_synthesized;
41 void* callback_user_data;
43
44static CallRecord* call_record_new(const FlutterKeyEvent* event,
46 void* callback_user_data) {
47 CallRecord* record = g_new0(CallRecord, 1);
48 record->event_type = event->type;
49 record->event_physical = event->physical;
50 record->event_logical = event->logical;
51 record->event_character = g_strdup(event->character);
52 record->event_synthesized = event->synthesized;
53 record->callback = callback;
54 record->callback_user_data = callback_user_data;
55 return record;
56}
57
58static void call_record_free(CallRecord* record) {
59 g_free(record->event_character);
60 g_free(record);
61}
62
63static void call_record_respond(CallRecord* record, bool handled) {
64 if (record->callback != nullptr) {
65 record->callback(handled, record->callback_user_data);
66 }
67}
68
69namespace {
70using ::flutter::testing::keycodes::kLogicalAltLeft;
71using ::flutter::testing::keycodes::kLogicalBracketLeft;
72using ::flutter::testing::keycodes::kLogicalComma;
73using ::flutter::testing::keycodes::kLogicalControlLeft;
74using ::flutter::testing::keycodes::kLogicalDigit1;
75using ::flutter::testing::keycodes::kLogicalKeyA;
76using ::flutter::testing::keycodes::kLogicalKeyB;
77using ::flutter::testing::keycodes::kLogicalKeyM;
78using ::flutter::testing::keycodes::kLogicalKeyQ;
79using ::flutter::testing::keycodes::kLogicalMetaLeft;
80using ::flutter::testing::keycodes::kLogicalMinus;
81using ::flutter::testing::keycodes::kLogicalParenthesisRight;
82using ::flutter::testing::keycodes::kLogicalSemicolon;
83using ::flutter::testing::keycodes::kLogicalShiftLeft;
84using ::flutter::testing::keycodes::kLogicalUnderscore;
85
86using ::flutter::testing::keycodes::kPhysicalAltLeft;
87using ::flutter::testing::keycodes::kPhysicalControlLeft;
88using ::flutter::testing::keycodes::kPhysicalKeyA;
89using ::flutter::testing::keycodes::kPhysicalKeyB;
90using ::flutter::testing::keycodes::kPhysicalMetaLeft;
91using ::flutter::testing::keycodes::kPhysicalShiftLeft;
92
93constexpr guint16 kKeyCodeKeyA = 0x26u;
94constexpr guint16 kKeyCodeKeyM = 0x3au;
95constexpr guint16 kKeyCodeDigit1 = 0x0au;
96constexpr guint16 kKeyCodeMinus = 0x14u;
97constexpr guint16 kKeyCodeSemicolon = 0x2fu;
98constexpr guint16 kKeyCodeKeyLeftBracket = 0x22u;
99
100// All key clues for a keyboard layout.
101//
102// The index is (keyCode * 2 + hasShift), where each value is the character for
103// this key (GTK only supports UTF-16.) Since the maximum keycode of interest
104// is 128, it has a total of 256 entries..
105typedef std::array<uint32_t, 256> MockGroupLayoutData;
106typedef std::vector<const MockGroupLayoutData*> MockLayoutData;
107
108extern const MockLayoutData kLayoutRussian;
109extern const MockLayoutData kLayoutFrench;
110
111class FlKeyboardManagerTest : public flutter::testing::LinuxTest {
112 protected:
113 void SetUp() override {
114 messenger = fl_mock_binary_messenger_new();
115 g_clear_object(&engine);
116 engine =
117 fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
119 StartEngine();
120 }
121
122 ~FlKeyboardManagerTest() {
123 g_clear_object(&manager);
124 g_clear_object(&messenger);
125 }
126
127 FlMockBinaryMessenger* messenger = nullptr;
128 FlKeyboardManager* manager = nullptr;
129 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
130};
131
132TEST_F(FlKeyboardManagerTest, EngineNoResponseChannelHandled) {
133 // Channel handles all events.
135 messenger, "flutter/keyevent",
136 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
137 gpointer user_data) {
138 g_autoptr(FlValue) return_value = fl_value_new_map();
139 fl_value_set_string_take(return_value, "handled",
141 return fl_value_ref(return_value);
142 },
143 nullptr);
144
145 // Don't handle first event - async call never completes.
147 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
149 void* user_data) { return kSuccess; }));
150 g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
151 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
152 gboolean first_event_completed = FALSE;
154 manager, event1, nullptr,
155 [](GObject* object, GAsyncResult* result, gpointer user_data) {
156 gboolean* first_event_completed = static_cast<gboolean*>(user_data);
157 *first_event_completed = TRUE;
158 },
159 &first_event_completed);
160
161 // Handle second event.
163 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
165 callback(true, user_data);
166 return kSuccess;
167 }));
168 g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
169 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
171 manager, event2, nullptr,
172 [](GObject* object, GAsyncResult* result, gpointer user_data) {
173 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
175 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
176 EXPECT_EQ(redispatched_event, nullptr);
177 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
178 },
179 loop);
180 g_main_loop_run(loop);
181
182 EXPECT_FALSE(first_event_completed);
183
184 // Passes if the cleanup does not crash.
185}
186
187TEST_F(FlKeyboardManagerTest, EngineHandledChannelNotHandledSync) {
188 // Handle channel and embedder calls synchronously.
190 messenger, "flutter/keyevent",
191 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
192 gpointer user_data) {
193 g_autoptr(FlValue) return_value = fl_value_new_map();
194 fl_value_set_string_take(return_value, "handled",
195 fl_value_new_bool(FALSE));
196 return fl_value_ref(return_value);
197 },
198 nullptr);
200 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
202 callback(true, user_data);
203 return kSuccess;
204 }));
205
206 g_autoptr(FlKeyEvent) event = fl_key_event_new(
207 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
209 manager, event, nullptr,
210 [](GObject* object, GAsyncResult* result, gpointer user_data) {
211 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
213 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
214 EXPECT_EQ(redispatched_event, nullptr);
215 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
216 },
217 loop);
218 g_main_loop_run(loop);
219}
220
221TEST_F(FlKeyboardManagerTest, EngineNotHandledChannelHandledSync) {
222 // Handle channel and embedder calls synchronously.
224 messenger, "flutter/keyevent",
225 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
226 gpointer user_data) {
227 g_autoptr(FlValue) return_value = fl_value_new_map();
228 fl_value_set_string_take(return_value, "handled",
230 return fl_value_ref(return_value);
231 },
232 nullptr);
234 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
236 callback(false, user_data);
237 return kSuccess;
238 }));
239
240 g_autoptr(FlKeyEvent) event = fl_key_event_new(
241 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
243 manager, event, nullptr,
244 [](GObject* object, GAsyncResult* result, gpointer user_data) {
245 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
247 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
248 EXPECT_EQ(redispatched_event, nullptr);
249 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
250 },
251 loop);
252 g_main_loop_run(loop);
253}
254
255TEST_F(FlKeyboardManagerTest, EngineHandledChannelHandledSync) {
256 // Handle channel and embedder calls synchronously.
258 messenger, "flutter/keyevent",
259 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
260 gpointer user_data) {
261 g_autoptr(FlValue) return_value = fl_value_new_map();
262 fl_value_set_string_take(return_value, "handled",
264 return fl_value_ref(return_value);
265 },
266 nullptr);
268 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
270 callback(true, user_data);
271 return kSuccess;
272 }));
273
274 g_autoptr(FlKeyEvent) event = fl_key_event_new(
275 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
277 manager, event, nullptr,
278 [](GObject* object, GAsyncResult* result, gpointer user_data) {
279 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
281 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
282 EXPECT_EQ(redispatched_event, nullptr);
283 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
284 },
285 loop);
286 g_main_loop_run(loop);
287}
288
289TEST_F(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledSync) {
290 // Handle channel and embedder calls synchronously.
292 messenger, "flutter/keyevent",
293 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
294 gpointer user_data) {
295 g_autoptr(FlValue) return_value = fl_value_new_map();
296 fl_value_set_string_take(return_value, "handled",
297 fl_value_new_bool(FALSE));
298 return fl_value_ref(return_value);
299 },
300 nullptr);
302 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
304 callback(false, user_data);
305 return kSuccess;
306 }));
307
308 g_autoptr(FlKeyEvent) event = fl_key_event_new(
309 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
311 manager, event, nullptr,
312 [](GObject* object, GAsyncResult* result, gpointer user_data) {
313 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
315 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
316 EXPECT_NE(redispatched_event, nullptr);
317 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
318 },
319 loop);
320 g_main_loop_run(loop);
321}
322
323static void channel_respond(FlMockBinaryMessenger* messenger,
324 GTask* task,
325 gboolean handled) {
327 fl_value_set_string_take(value, "handled", fl_value_new_bool(handled));
329}
330
331TEST_F(FlKeyboardManagerTest, EngineHandledChannelNotHandledAsync) {
332 // Handle channel and embedder calls asynchronously.
333 g_autoptr(GPtrArray) channel_calls =
334 g_ptr_array_new_with_free_func(g_object_unref);
336 messenger, "flutter/keyevent",
337 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
338 gpointer user_data) {
339 GPtrArray* channel_calls = static_cast<GPtrArray*>(user_data);
340 g_ptr_array_add(channel_calls, g_object_ref(task));
341 // Will respond async
342 return static_cast<FlValue*>(nullptr);
343 },
344 channel_calls);
345 g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
346 reinterpret_cast<GDestroyNotify>(call_record_free));
348 SendKeyEvent, ([&embedder_call_records](
349 auto engine, const FlutterKeyEvent* event,
351 g_ptr_array_add(embedder_call_records,
353 return kSuccess;
354 }));
355
356 g_autoptr(FlKeyEvent) event = fl_key_event_new(
357 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
359 manager, event, nullptr,
360 [](GObject* object, GAsyncResult* result, gpointer user_data) {
361 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
363 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
364 EXPECT_EQ(redispatched_event, nullptr);
365 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
366 },
367 loop);
368
369 EXPECT_EQ(embedder_call_records->len, 1u);
370 EXPECT_EQ(channel_calls->len, 1u);
371
373 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
374 true);
375 channel_respond(messenger,
376 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
377 FALSE);
378 g_main_loop_run(loop);
379}
380
381TEST_F(FlKeyboardManagerTest, EngineNotHandledChannelHandledAsync) {
382 // Handle channel and embedder calls asynchronously.
383 g_autoptr(GPtrArray) channel_calls =
384 g_ptr_array_new_with_free_func(g_object_unref);
386 messenger, "flutter/keyevent",
387 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
388 gpointer user_data) {
389 GPtrArray* channel_calls = static_cast<GPtrArray*>(user_data);
390 g_ptr_array_add(channel_calls, g_object_ref(task));
391 // Will respond async
392 return static_cast<FlValue*>(nullptr);
393 },
394 channel_calls);
395 g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
396 reinterpret_cast<GDestroyNotify>(call_record_free));
398 SendKeyEvent, ([&embedder_call_records](
399 auto engine, const FlutterKeyEvent* event,
401 g_ptr_array_add(embedder_call_records,
403 return kSuccess;
404 }));
405
406 g_autoptr(FlKeyEvent) event = fl_key_event_new(
407 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
409 manager, event, nullptr,
410 [](GObject* object, GAsyncResult* result, gpointer user_data) {
411 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
413 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
414 EXPECT_EQ(redispatched_event, nullptr);
415 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
416 },
417 loop);
418
419 EXPECT_EQ(embedder_call_records->len, 1u);
420 EXPECT_EQ(channel_calls->len, 1u);
421
423 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
424 false);
425 channel_respond(messenger,
426 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
427 TRUE);
428 g_main_loop_run(loop);
429}
430
431TEST_F(FlKeyboardManagerTest, EngineHandledChannelHandledAsync) {
432 // Handle channel and embedder calls asynchronously.
433 g_autoptr(GPtrArray) channel_calls =
434 g_ptr_array_new_with_free_func(g_object_unref);
436 messenger, "flutter/keyevent",
437 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
438 gpointer user_data) {
439 GPtrArray* channel_calls = static_cast<GPtrArray*>(user_data);
440 g_ptr_array_add(channel_calls, g_object_ref(task));
441 // Will respond async
442 return static_cast<FlValue*>(nullptr);
443 },
444 channel_calls);
445 g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
446 reinterpret_cast<GDestroyNotify>(call_record_free));
448 SendKeyEvent, ([&embedder_call_records](
449 auto engine, const FlutterKeyEvent* event,
451 g_ptr_array_add(embedder_call_records,
453 return kSuccess;
454 }));
455
456 g_autoptr(FlKeyEvent) event = fl_key_event_new(
457 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
459 manager, event, nullptr,
460 [](GObject* object, GAsyncResult* result, gpointer user_data) {
461 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
463 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
464 EXPECT_EQ(redispatched_event, nullptr);
465 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
466 },
467 loop);
468
469 EXPECT_EQ(embedder_call_records->len, 1u);
470 EXPECT_EQ(channel_calls->len, 1u);
471
473 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
474 true);
475 channel_respond(messenger,
476 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
477 TRUE);
478 g_main_loop_run(loop);
479}
480
481TEST_F(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledAsync) {
482 // Handle channel and embedder calls asynchronously.
483 g_autoptr(GPtrArray) channel_calls =
484 g_ptr_array_new_with_free_func(g_object_unref);
486 messenger, "flutter/keyevent",
487 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
488 gpointer user_data) {
489 GPtrArray* channel_calls = static_cast<GPtrArray*>(user_data);
490 g_ptr_array_add(channel_calls, g_object_ref(task));
491 // Will respond async
492 return static_cast<FlValue*>(nullptr);
493 },
494 channel_calls);
495 g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
496 reinterpret_cast<GDestroyNotify>(call_record_free));
498 SendKeyEvent, ([&embedder_call_records](
499 auto engine, const FlutterKeyEvent* event,
501 g_ptr_array_add(embedder_call_records,
503 return kSuccess;
504 }));
505
506 g_autoptr(FlKeyEvent) event = fl_key_event_new(
507 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
509 manager, event, nullptr,
510 [](GObject* object, GAsyncResult* result, gpointer user_data) {
511 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
513 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
514 EXPECT_NE(redispatched_event, nullptr);
515 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
516 },
517 loop);
518
519 EXPECT_EQ(embedder_call_records->len, 1u);
520 EXPECT_EQ(channel_calls->len, 1u);
521
523 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
524 false);
525 channel_respond(messenger,
526 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
527 FALSE);
528 g_main_loop_run(loop);
529}
530
531TEST_F(FlKeyboardManagerTest, CorrectLogicalKeyForLayouts) {
532 g_autoptr(GPtrArray) call_records = g_ptr_array_new_with_free_func(
533 reinterpret_cast<GDestroyNotify>(call_record_free));
535 SendKeyEvent,
536 ([&call_records](auto engine, const FlutterKeyEvent* event,
538 g_ptr_array_add(call_records,
540 return kSuccess;
541 }));
542
543 // Respond to the key event channel so its async messages complete instead of
544 // leaking their GTasks.
546 messenger, "flutter/keyevent",
547 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
548 gpointer user_data) {
549 g_autoptr(FlValue) return_value = fl_value_new_map();
550 fl_value_set_string_take(return_value, "handled",
551 fl_value_new_bool(FALSE));
552 return fl_value_ref(return_value);
553 },
554 nullptr);
555
556 auto sendTap = [&](guint8 keycode, guint keyval, guint8 group) {
557 g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
558 0, TRUE, keycode, keyval, static_cast<GdkModifierType>(0), group);
559 fl_keyboard_manager_handle_event(manager, event1, nullptr, nullptr,
560 nullptr);
561 g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
562 0, FALSE, keycode, keyval, static_cast<GdkModifierType>(0), group);
563 fl_keyboard_manager_handle_event(manager, event2, nullptr, nullptr,
564 nullptr);
565 };
566
567 /* US keyboard layout */
568
569 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
570 VERIFY_DOWN(kLogicalKeyA, "a");
571
572 sendTap(kKeyCodeKeyA, GDK_KEY_A, 0); // Shift-KeyA
573 VERIFY_DOWN(kLogicalKeyA, "A");
574
575 sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
576 VERIFY_DOWN(kLogicalDigit1, "1");
577
578 sendTap(kKeyCodeDigit1, GDK_KEY_exclam, 0); // Shift-Digit1
579 VERIFY_DOWN(kLogicalDigit1, "!");
580
581 sendTap(kKeyCodeMinus, GDK_KEY_minus, 0); // Minus
582 VERIFY_DOWN(kLogicalMinus, "-");
583
584 sendTap(kKeyCodeMinus, GDK_KEY_underscore, 0); // Shift-Minus
585 VERIFY_DOWN(kLogicalUnderscore, "_");
586
587 /* French keyboard layout, group 3, which is when the input method is showing
588 * "Fr" */
589
591 manager,
592 [](const GdkKeymapKey* key, gpointer user_data) {
593 MockLayoutData* layout_data = static_cast<MockLayoutData*>(user_data);
594 guint8 group = static_cast<guint8>(key->group);
595 EXPECT_LT(group, layout_data->size());
596 const MockGroupLayoutData* group_layout = (*layout_data)[group];
597 EXPECT_NE(group_layout, nullptr);
598 EXPECT_TRUE(key->level == 0 || key->level == 1);
599 bool shift = key->level == 1;
600 return (*group_layout)[key->keycode * 2 + shift];
601 },
602 reinterpret_cast<gpointer>(const_cast<MockLayoutData*>(&kLayoutFrench)));
603
604 sendTap(kKeyCodeKeyA, GDK_KEY_q, 3); // KeyA
605 VERIFY_DOWN(kLogicalKeyQ, "q");
606
607 sendTap(kKeyCodeKeyA, GDK_KEY_Q, 3); // Shift-KeyA
608 VERIFY_DOWN(kLogicalKeyQ, "Q");
609
610 sendTap(kKeyCodeSemicolon, GDK_KEY_m, 3); // ; but prints M
611 VERIFY_DOWN(kLogicalKeyM, "m");
612
613 sendTap(kKeyCodeKeyM, GDK_KEY_comma, 3); // M but prints ,
614 VERIFY_DOWN(kLogicalComma, ",");
615
616 sendTap(kKeyCodeDigit1, GDK_KEY_ampersand, 3); // Digit1
617 VERIFY_DOWN(kLogicalDigit1, "&");
618
619 sendTap(kKeyCodeDigit1, GDK_KEY_1, 3); // Shift-Digit1
620 VERIFY_DOWN(kLogicalDigit1, "1");
621
622 sendTap(kKeyCodeMinus, GDK_KEY_parenright, 3); // Minus
623 VERIFY_DOWN(kLogicalParenthesisRight, ")");
624
625 sendTap(kKeyCodeMinus, GDK_KEY_degree, 3); // Shift-Minus
626 VERIFY_DOWN(static_cast<uint32_t>(L'°'), "°");
627
628 /* French keyboard layout, group 0, which is pressing the "extra key for
629 * triggering input method" key once after switching to French IME. */
630
631 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
632 VERIFY_DOWN(kLogicalKeyA, "a");
633
634 sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
635 VERIFY_DOWN(kLogicalDigit1, "1");
636
637 /* Russian keyboard layout, group 2 */
639 manager,
640 [](const GdkKeymapKey* key, gpointer user_data) {
641 MockLayoutData* layout_data = static_cast<MockLayoutData*>(user_data);
642 guint8 group = static_cast<guint8>(key->group);
643 EXPECT_LT(group, layout_data->size());
644 const MockGroupLayoutData* group_layout = (*layout_data)[group];
645 EXPECT_NE(group_layout, nullptr);
646 EXPECT_TRUE(key->level == 0 || key->level == 1);
647 bool shift = key->level == 1;
648 return (*group_layout)[key->keycode * 2 + shift];
649 },
650 reinterpret_cast<gpointer>(const_cast<MockLayoutData*>(&kLayoutRussian)));
651
652 sendTap(kKeyCodeKeyA, GDK_KEY_Cyrillic_ef, 2); // KeyA
653 VERIFY_DOWN(kLogicalKeyA, "Ñ„");
654
655 sendTap(kKeyCodeDigit1, GDK_KEY_1, 2); // Shift-Digit1
656 VERIFY_DOWN(kLogicalDigit1, "1");
657
658 sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_Cyrillic_ha, 2);
659 VERIFY_DOWN(kLogicalBracketLeft, "Ñ…");
660
661 /* Russian keyboard layout, group 0 */
662 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
663 VERIFY_DOWN(kLogicalKeyA, "a");
664
665 sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_bracketleft, 0);
666 VERIFY_DOWN(kLogicalBracketLeft, "[");
667}
668
669TEST_F(FlKeyboardManagerTest, SynthesizeModifiersIfNeeded) {
670 g_autoptr(GPtrArray) call_records = g_ptr_array_new_with_free_func(
671 reinterpret_cast<GDestroyNotify>(call_record_free));
673 SendKeyEvent,
674 ([&call_records](auto engine, const FlutterKeyEvent* event,
676 g_ptr_array_add(call_records,
678 return kSuccess;
679 }));
680
681 auto verifyModifierIsSynthesized = [&](GdkModifierType mask,
682 uint64_t physical, uint64_t logical) {
683 // Modifier is pressed.
684 guint state = mask;
686 EXPECT_EQ(call_records->len, 1u);
687 CallRecord* record =
688 static_cast<CallRecord*>(g_ptr_array_index(call_records, 0));
689 EXPECT_EQ(record->event_type, kFlutterKeyEventTypeDown);
690 EXPECT_EQ(record->event_physical, physical);
691 EXPECT_EQ(record->event_logical, logical);
692 EXPECT_STREQ(record->event_character, NULL);
693 EXPECT_EQ(record->event_synthesized, true);
694 // Modifier is released.
695 state = state ^ mask;
697 EXPECT_EQ(call_records->len, 2u);
698 record = static_cast<CallRecord*>(g_ptr_array_index(call_records, 1));
699 EXPECT_EQ(record->event_type, kFlutterKeyEventTypeUp);
700 EXPECT_EQ(record->event_physical, physical);
701 EXPECT_EQ(record->event_logical, logical);
702 EXPECT_STREQ(record->event_character, NULL);
703 EXPECT_EQ(record->event_synthesized, true);
704 g_ptr_array_set_size(call_records, 0);
705 };
706
707 // No modifiers pressed.
708 guint state = 0;
710 EXPECT_EQ(call_records->len, 0u);
711 g_ptr_array_set_size(call_records, 0);
712
713 // Press and release each modifier once.
714 verifyModifierIsSynthesized(GDK_CONTROL_MASK, kPhysicalControlLeft,
715 kLogicalControlLeft);
716 verifyModifierIsSynthesized(GDK_META_MASK, kPhysicalMetaLeft,
717 kLogicalMetaLeft);
718 verifyModifierIsSynthesized(GDK_MOD1_MASK, kPhysicalAltLeft, kLogicalAltLeft);
719 verifyModifierIsSynthesized(GDK_SHIFT_MASK, kPhysicalShiftLeft,
720 kLogicalShiftLeft);
721}
722
723TEST_F(FlKeyboardManagerTest, GetPressedState) {
724 // Dispatch a key event.
726 messenger, "flutter/keyevent",
727 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
728 gpointer user_data) {
729 FlValue* response = fl_value_new_map();
730 fl_value_set_string_take(response, "handled", fl_value_new_bool(FALSE));
731 return response;
732 },
733 nullptr);
735 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
737 callback(false, user_data);
738 return kSuccess;
739 }));
740 g_autoptr(FlKeyEvent) event = fl_key_event_new(
741 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
743 manager, event, nullptr,
744 [](GObject* object, GAsyncResult* result, gpointer user_data) {
745 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
747 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
748 EXPECT_NE(redispatched_event, nullptr);
749 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
750 },
751 loop);
752 g_main_loop_run(loop);
753
754 GHashTable* pressed_state = fl_keyboard_manager_get_pressed_state(manager);
755 EXPECT_EQ(g_hash_table_size(pressed_state), 1u);
756
757 gpointer physical_key =
758 g_hash_table_lookup(pressed_state, uint64_to_gpointer(kPhysicalKeyA));
759 EXPECT_EQ(gpointer_to_uint64(physical_key), kLogicalKeyA);
760}
761
762// The following layout data is generated using DEBUG_PRINT_LAYOUT.
763
764const MockGroupLayoutData kLayoutRussian0{
765 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
766 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
767 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
768 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
769 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
770 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
771 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
772 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
773 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
774 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
775 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
776 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
777 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
778 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
779 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
780 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
781 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
782 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
783 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
784 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
785 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
786 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
787 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
788 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
789 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
790 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
791 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
792 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
793 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
794 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
795 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
796 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
797 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
798};
799
800const MockGroupLayoutData kLayoutRussian2{{
801 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
802 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
803 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
804 0xffff, 0x0031, 0x0021, 0x0000, 0x0031, 0x0021, 0x0032, 0x0022, // 0x08
805 0x0033, 0x06b0, 0x0034, 0x003b, 0x0035, 0x0025, 0x0036, 0x003a, // 0x0c
806 0x0037, 0x003f, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
807 0x002d, 0x005f, 0x003d, 0x002b, 0x0071, 0x0051, 0x0000, 0x0000, // 0x14
808 0x06ca, 0x06ea, 0x06c3, 0x06e3, 0x06d5, 0x06f5, 0x06cb, 0x06eb, // 0x18
809 0x06c5, 0x06e5, 0x06ce, 0x06ee, 0x06c7, 0x06e7, 0x06db, 0x06fb, // 0x1c
810 0x06dd, 0x06fd, 0x06da, 0x06fa, 0x06c8, 0x06e8, 0x06df, 0x06ff, // 0x20
811 0x0061, 0x0041, 0x0041, 0x0000, 0x06c6, 0x06e6, 0x06d9, 0x06f9, // 0x24
812 0x06d7, 0x06f7, 0x06c1, 0x06e1, 0x06d0, 0x06f0, 0x06d2, 0x06f2, // 0x28
813 0x06cf, 0x06ef, 0x06cc, 0x06ec, 0x06c4, 0x06e4, 0x06d6, 0x06f6, // 0x2c
814 0x06dc, 0x06fc, 0x06a3, 0x06b3, 0x007c, 0x0000, 0x005c, 0x002f, // 0x30
815 0x06d1, 0x06f1, 0x06de, 0x06fe, 0x06d3, 0x06f3, 0x06cd, 0x06ed, // 0x34
816 0x06c9, 0x06e9, 0x06d4, 0x06f4, 0x06d8, 0x06f8, 0x06c2, 0x06e2, // 0x38
817 0x06c0, 0x06e0, 0x002e, 0x002c, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
818 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
819 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
820 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
821 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
822 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
823 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
824 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
825 0xffff, 0xffff, 0x003c, 0x003e, 0x002f, 0x007c, 0xffff, 0xffff, // 0x5c
826 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
827 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x64
828 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0x0000, // 0x68
829 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
830 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
831 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
832 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, // 0x78
833 0x00b1, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x7c
834}};
835
836const MockGroupLayoutData kLayoutFrench0 = {
837 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
838 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
839 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
840 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
841 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
842 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
843 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
844 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
845 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
846 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
847 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
848 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
849 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
850 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
851 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
852 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
853 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
854 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
855 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
856 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
857 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
858 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
859 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
860 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
861 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
862 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
863 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
864 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
865 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
866 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
867 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
868 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
869 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
870};
871
872const MockGroupLayoutData kLayoutFrench3 = {
873 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
874 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
875 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
876 0x0000, 0xffff, 0x0000, 0x0000, 0x0026, 0x0031, 0x00e9, 0x0032, // 0x08
877 0x0022, 0x0033, 0x0027, 0x0034, 0x0028, 0x0035, 0x002d, 0x0036, // 0x0c
878 0x00e8, 0x0037, 0x005f, 0x0038, 0x00e7, 0x0039, 0x00e0, 0x0030, // 0x10
879 0x0029, 0x00b0, 0x003d, 0x002b, 0x0000, 0x0000, 0x0061, 0x0041, // 0x14
880 0x0061, 0x0041, 0x007a, 0x005a, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
881 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
882 0x006f, 0x004f, 0x0070, 0x0050, 0xffff, 0xffff, 0x0024, 0x00a3, // 0x20
883 0x0041, 0x0000, 0x0000, 0x0000, 0x0071, 0x0051, 0x0073, 0x0053, // 0x24
884 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
885 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x006d, 0x004d, // 0x2c
886 0x00f9, 0x0025, 0x00b2, 0x007e, 0x0000, 0x0000, 0x002a, 0x00b5, // 0x30
887 0x0077, 0x0057, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
888 0x0062, 0x0042, 0x006e, 0x004e, 0x002c, 0x003f, 0x003b, 0x002e, // 0x38
889 0x003a, 0x002f, 0x0021, 0x00a7, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
890 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
891 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
892 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
893 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
894 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
895 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
896 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
897 0xffff, 0x003c, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
898 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
899 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
900 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
901 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
902 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
903 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
904 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, // 0x78
905 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x7c
906};
907
908const MockLayoutData kLayoutRussian{&kLayoutRussian0, nullptr,
909 &kLayoutRussian2};
910const MockLayoutData kLayoutFrench{&kLayoutFrench0, nullptr, nullptr,
911 &kLayoutFrench3};
912
913} // namespace
void StartEngine(FlEngine *engine=nullptr)
Definition linux_test.cc:29
int32_t value
@ kSuccess
Definition embedder.h:73
void(* FlutterKeyEventCallback)(bool, void *)
Definition embedder.h:1482
FlutterKeyEventType
Definition embedder.h:1401
@ kFlutterKeyEventTypeUp
Definition embedder.h:1402
FlutterEngine engine
Definition main.cc:84
const char * message
g_autoptr(FlEngine) engine
return TRUE
FlEngine * fl_engine_new_with_binary_messenger(FlBinaryMessenger *binary_messenger)
Definition fl_engine.cc:734
FlutterEngineProcTable * fl_engine_get_embedder_api(FlEngine *self)
Definition fl_engine.cc:920
FlKeyEvent * fl_key_event_new(guint32 time, gboolean is_press, guint16 keycode, guint keyval, GdkModifierType state, guint8 group)
gpointer uint64_to_gpointer(uint64_t number)
uint64_t gpointer_to_uint64(gpointer pointer)
constexpr guint16 kKeyCodeKeyA
void fl_keyboard_manager_handle_event(FlKeyboardManager *self, FlKeyEvent *event, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
GHashTable * fl_keyboard_manager_get_pressed_state(FlKeyboardManager *self)
gboolean fl_keyboard_manager_handle_event_finish(FlKeyboardManager *self, GAsyncResult *result, FlKeyEvent **redispatched_event, GError **error)
void fl_keyboard_manager_set_lookup_key_handler(FlKeyboardManager *self, FlKeyboardManagerLookupKeyHandler lookup_key_handler, gpointer user_data)
FlKeyboardManager * fl_keyboard_manager_new(FlEngine *engine)
void fl_keyboard_manager_sync_modifier_if_needed(FlKeyboardManager *self, guint state, double event_time)
static void call_record_respond(CallRecord *record, bool handled)
static void call_record_free(CallRecord *record)
#define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR)
static CallRecord * call_record_new(const FlutterKeyEvent *event, FlutterKeyEventCallback callback, void *callback_user_data)
FlMockBinaryMessenger * fl_mock_binary_messenger_new()
void fl_mock_binary_messenger_json_message_channel_respond(FlMockBinaryMessenger *self, GTask *task, FlValue *value)
void fl_mock_binary_messenger_set_json_message_channel(FlMockBinaryMessenger *self, const gchar *channel, FlMockBinaryMessengerMessageChannelHandler handler, gpointer user_data)
G_MODULE_EXPORT FlValue * fl_value_new_map()
Definition fl_value.cc:366
G_MODULE_EXPORT FlValue * fl_value_ref(FlValue *self)
Definition fl_value.cc:394
G_MODULE_EXPORT void fl_value_set_string_take(FlValue *self, const gchar *key, FlValue *value)
Definition fl_value.cc:650
G_MODULE_EXPORT FlValue * fl_value_new_bool(bool value)
Definition fl_value.cc:255
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition fl_value.h:42
FlutterDesktopBinaryReply callback
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font manager
#define MOCK_ENGINE_PROC(proc, mock_impl)
FlutterKeyEventCallback callback
FlutterKeyEventType event_type
FlutterEngineSendKeyEventFnPtr SendKeyEvent
Definition embedder.h:3776
const char * character
Definition embedder.h:1464

Function Documentation

◆ call_record_free()

static void call_record_free ( CallRecord record)
static

Definition at line 59 of file fl_keyboard_manager_test.cc.

59 {
60 g_free(record->event_character);
61 g_free(record);
62}

References CallRecord::event_character.

◆ call_record_new()

static CallRecord * call_record_new ( const FlutterKeyEvent event,
FlutterKeyEventCallback  callback,
void *  callback_user_data 
)
static

Definition at line 45 of file fl_keyboard_manager_test.cc.

47 {
48 CallRecord* record = g_new0(CallRecord, 1);
49 record->event_type = event->type;
50 record->event_physical = event->physical;
51 record->event_logical = event->logical;
52 record->event_character = g_strdup(event->character);
53 record->event_synthesized = event->synthesized;
54 record->callback = callback;
55 record->callback_user_data = callback_user_data;
56 return record;
57}

References CallRecord::callback, callback, CallRecord::callback_user_data, FlutterKeyEvent::character, CallRecord::event_character, CallRecord::event_logical, CallRecord::event_physical, CallRecord::event_synthesized, and CallRecord::event_type.

◆ call_record_respond()

static void call_record_respond ( CallRecord record,
bool  handled 
)
static

Definition at line 64 of file fl_keyboard_manager_test.cc.

64 {
65 if (record->callback != nullptr) {
66 record->callback(handled, record->callback_user_data);
67 }
68}

References CallRecord::callback, and CallRecord::callback_user_data.