Flutter Engine
 
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:1348

Definition at line 19 of file fl_keyboard_manager_test.cc.

33 {
35 uint64_t event_physical;
36 uint64_t event_logical;
37 gchar* event_character;
38 bool event_synthesized;
40 void* callback_user_data;
42
43static CallRecord* call_record_new(const FlutterKeyEvent* event,
45 void* callback_user_data) {
46 CallRecord* record = g_new0(CallRecord, 1);
47 record->event_type = event->type;
48 record->event_physical = event->physical;
49 record->event_logical = event->logical;
50 record->event_character = g_strdup(event->character);
51 record->event_synthesized = event->synthesized;
52 record->callback = callback;
53 record->callback_user_data = callback_user_data;
54 return record;
55}
56
57static void call_record_free(CallRecord* record) {
58 g_free(record->event_character);
59 g_free(record);
60}
61
62static void call_record_respond(CallRecord* record, bool handled) {
63 if (record->callback != nullptr) {
64 record->callback(handled, record->callback_user_data);
65 }
66}
67
68namespace {
69using ::flutter::testing::keycodes::kLogicalAltLeft;
70using ::flutter::testing::keycodes::kLogicalBracketLeft;
71using ::flutter::testing::keycodes::kLogicalComma;
72using ::flutter::testing::keycodes::kLogicalControlLeft;
73using ::flutter::testing::keycodes::kLogicalDigit1;
74using ::flutter::testing::keycodes::kLogicalKeyA;
75using ::flutter::testing::keycodes::kLogicalKeyB;
76using ::flutter::testing::keycodes::kLogicalKeyM;
77using ::flutter::testing::keycodes::kLogicalKeyQ;
78using ::flutter::testing::keycodes::kLogicalMetaLeft;
79using ::flutter::testing::keycodes::kLogicalMinus;
80using ::flutter::testing::keycodes::kLogicalParenthesisRight;
81using ::flutter::testing::keycodes::kLogicalSemicolon;
82using ::flutter::testing::keycodes::kLogicalShiftLeft;
83using ::flutter::testing::keycodes::kLogicalUnderscore;
84
85using ::flutter::testing::keycodes::kPhysicalAltLeft;
86using ::flutter::testing::keycodes::kPhysicalControlLeft;
87using ::flutter::testing::keycodes::kPhysicalKeyA;
88using ::flutter::testing::keycodes::kPhysicalKeyB;
89using ::flutter::testing::keycodes::kPhysicalMetaLeft;
90using ::flutter::testing::keycodes::kPhysicalShiftLeft;
91
92constexpr guint16 kKeyCodeKeyA = 0x26u;
93constexpr guint16 kKeyCodeKeyM = 0x3au;
94constexpr guint16 kKeyCodeDigit1 = 0x0au;
95constexpr guint16 kKeyCodeMinus = 0x14u;
96constexpr guint16 kKeyCodeSemicolon = 0x2fu;
97constexpr guint16 kKeyCodeKeyLeftBracket = 0x22u;
98
99// All key clues for a keyboard layout.
100//
101// The index is (keyCode * 2 + hasShift), where each value is the character for
102// this key (GTK only supports UTF-16.) Since the maximum keycode of interest
103// is 128, it has a total of 256 entries..
104typedef std::array<uint32_t, 256> MockGroupLayoutData;
105typedef std::vector<const MockGroupLayoutData*> MockLayoutData;
106
107extern const MockLayoutData kLayoutRussian;
108extern const MockLayoutData kLayoutFrench;
109
110TEST(FlKeyboardManagerTest, EngineNoResponseChannelHandled) {
111 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
112
113 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
114 // Channel handles all events.
116 messenger, "flutter/keyevent",
117 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
118 gpointer user_data) {
119 g_autoptr(FlValue) return_value = fl_value_new_map();
120 fl_value_set_string_take(return_value, "handled",
122 return fl_value_ref(return_value);
123 },
124 nullptr);
125
126 g_autoptr(FlEngine) engine =
127 fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
128 g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
129
130 EXPECT_TRUE(fl_engine_start(engine, nullptr));
131
132 // Don't handle first event - async call never completes.
134 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
136 void* user_data) { return kSuccess; }));
137 g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
138 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
139 gboolean first_event_completed = FALSE;
141 manager, event1, nullptr,
142 [](GObject* object, GAsyncResult* result, gpointer user_data) {
143 gboolean* first_event_completed = static_cast<gboolean*>(user_data);
144 *first_event_completed = TRUE;
145 },
146 &first_event_completed);
147
148 // Handle second event.
150 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
152 callback(true, user_data);
153 return kSuccess;
154 }));
155 g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
156 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
157 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
159 manager, event2, nullptr,
160 [](GObject* object, GAsyncResult* result, gpointer user_data) {
161 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
163 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
164 EXPECT_EQ(redispatched_event, nullptr);
165 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
166 },
167 loop);
168 g_main_loop_run(loop);
169
170 EXPECT_FALSE(first_event_completed);
171
172 // Passes if the cleanup does not crash.
173}
174
175TEST(FlKeyboardManagerTest, EngineHandledChannelNotHandledSync) {
176 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
177
178 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
179
180 g_autoptr(FlEngine) engine =
181 fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
182 g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
183
184 EXPECT_TRUE(fl_engine_start(engine, nullptr));
185
186 // Handle channel and embedder calls synchronously.
188 messenger, "flutter/keyevent",
189 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
190 gpointer user_data) {
191 g_autoptr(FlValue) return_value = fl_value_new_map();
192 fl_value_set_string_take(return_value, "handled",
193 fl_value_new_bool(FALSE));
194 return fl_value_ref(return_value);
195 },
196 nullptr);
198 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
200 callback(true, user_data);
201 return kSuccess;
202 }));
203
204 g_autoptr(FlKeyEvent) event = fl_key_event_new(
205 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
206 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
208 manager, event, nullptr,
209 [](GObject* object, GAsyncResult* result, gpointer user_data) {
210 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
212 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
213 EXPECT_EQ(redispatched_event, nullptr);
214 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
215 },
216 loop);
217 g_main_loop_run(loop);
218}
219
220TEST(FlKeyboardManagerTest, EngineNotHandledChannelHandledSync) {
221 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
222
223 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
224
225 g_autoptr(FlEngine) engine =
226 fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
227 g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
228
229 EXPECT_TRUE(fl_engine_start(engine, nullptr));
230
231 // Handle channel and embedder calls synchronously.
233 messenger, "flutter/keyevent",
234 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
235 gpointer user_data) {
236 g_autoptr(FlValue) return_value = fl_value_new_map();
237 fl_value_set_string_take(return_value, "handled",
239 return fl_value_ref(return_value);
240 },
241 nullptr);
243 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
245 callback(false, user_data);
246 return kSuccess;
247 }));
248
249 g_autoptr(FlKeyEvent) event = fl_key_event_new(
250 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
251 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
253 manager, event, nullptr,
254 [](GObject* object, GAsyncResult* result, gpointer user_data) {
255 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
257 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
258 EXPECT_EQ(redispatched_event, nullptr);
259 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
260 },
261 loop);
262 g_main_loop_run(loop);
263}
264
265TEST(FlKeyboardManagerTest, EngineHandledChannelHandledSync) {
266 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
267
268 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
269
270 g_autoptr(FlEngine) engine =
271 fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
272 g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
273
274 EXPECT_TRUE(fl_engine_start(engine, nullptr));
275
276 // Handle channel and embedder calls synchronously.
278 messenger, "flutter/keyevent",
279 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
280 gpointer user_data) {
281 g_autoptr(FlValue) return_value = fl_value_new_map();
282 fl_value_set_string_take(return_value, "handled",
284 return fl_value_ref(return_value);
285 },
286 nullptr);
288 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
290 callback(true, user_data);
291 return kSuccess;
292 }));
293
294 g_autoptr(FlKeyEvent) event = fl_key_event_new(
295 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
296 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
298 manager, event, nullptr,
299 [](GObject* object, GAsyncResult* result, gpointer user_data) {
300 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
302 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
303 EXPECT_EQ(redispatched_event, nullptr);
304 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
305 },
306 loop);
307 g_main_loop_run(loop);
308}
309
310TEST(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledSync) {
311 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
312
313 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
314
315 g_autoptr(FlEngine) engine =
316 fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
317 g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
318
319 EXPECT_TRUE(fl_engine_start(engine, nullptr));
320
321 // Handle channel and embedder calls synchronously.
323 messenger, "flutter/keyevent",
324 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
325 gpointer user_data) {
326 g_autoptr(FlValue) return_value = fl_value_new_map();
327 fl_value_set_string_take(return_value, "handled",
328 fl_value_new_bool(FALSE));
329 return fl_value_ref(return_value);
330 },
331 nullptr);
333 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
335 callback(false, user_data);
336 return kSuccess;
337 }));
338
339 g_autoptr(FlKeyEvent) event = fl_key_event_new(
340 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
341 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
343 manager, event, nullptr,
344 [](GObject* object, GAsyncResult* result, gpointer user_data) {
345 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
347 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
348 EXPECT_NE(redispatched_event, nullptr);
349 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
350 },
351 loop);
352 g_main_loop_run(loop);
353}
354
355static void channel_respond(FlMockBinaryMessenger* messenger,
356 GTask* task,
357 gboolean handled) {
359 fl_value_set_string_take(value, "handled", fl_value_new_bool(handled));
361}
362
363TEST(FlKeyboardManagerTest, EngineHandledChannelNotHandledAsync) {
364 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
365
366 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
367
368 g_autoptr(FlEngine) engine =
369 fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
370 g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
371
372 EXPECT_TRUE(fl_engine_start(engine, nullptr));
373
374 // Handle channel and embedder calls asynchronously.
375 g_autoptr(GPtrArray) channel_calls =
376 g_ptr_array_new_with_free_func(g_object_unref);
378 messenger, "flutter/keyevent",
379 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
380 gpointer user_data) {
381 GPtrArray* channel_calls = static_cast<GPtrArray*>(user_data);
382 g_ptr_array_add(channel_calls, g_object_ref(task));
383 // Will respond async
384 return static_cast<FlValue*>(nullptr);
385 },
386 channel_calls);
387 g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
388 reinterpret_cast<GDestroyNotify>(call_record_free));
390 SendKeyEvent, ([&embedder_call_records](
391 auto engine, const FlutterKeyEvent* event,
393 g_ptr_array_add(embedder_call_records,
395 return kSuccess;
396 }));
397
398 g_autoptr(FlKeyEvent) event = fl_key_event_new(
399 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
400 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
402 manager, event, nullptr,
403 [](GObject* object, GAsyncResult* result, gpointer user_data) {
404 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
406 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
407 EXPECT_EQ(redispatched_event, nullptr);
408 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
409 },
410 loop);
411
412 EXPECT_EQ(embedder_call_records->len, 1u);
413 EXPECT_EQ(channel_calls->len, 1u);
414
416 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
417 true);
418 channel_respond(messenger,
419 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
420 FALSE);
421 g_main_loop_run(loop);
422}
423
424TEST(FlKeyboardManagerTest, EngineNotHandledChannelHandledAsync) {
425 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
426
427 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
428
429 g_autoptr(FlEngine) engine =
430 fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
431 g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
432
433 EXPECT_TRUE(fl_engine_start(engine, nullptr));
434
435 // Handle channel and embedder calls asynchronously.
436 g_autoptr(GPtrArray) channel_calls =
437 g_ptr_array_new_with_free_func(g_object_unref);
439 messenger, "flutter/keyevent",
440 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
441 gpointer user_data) {
442 GPtrArray* channel_calls = static_cast<GPtrArray*>(user_data);
443 g_ptr_array_add(channel_calls, g_object_ref(task));
444 // Will respond async
445 return static_cast<FlValue*>(nullptr);
446 },
447 channel_calls);
448 g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
449 reinterpret_cast<GDestroyNotify>(call_record_free));
451 SendKeyEvent, ([&embedder_call_records](
452 auto engine, const FlutterKeyEvent* event,
454 g_ptr_array_add(embedder_call_records,
456 return kSuccess;
457 }));
458
459 g_autoptr(FlKeyEvent) event = fl_key_event_new(
460 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
461 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
463 manager, event, nullptr,
464 [](GObject* object, GAsyncResult* result, gpointer user_data) {
465 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
467 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
468 EXPECT_EQ(redispatched_event, nullptr);
469 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
470 },
471 loop);
472
473 EXPECT_EQ(embedder_call_records->len, 1u);
474 EXPECT_EQ(channel_calls->len, 1u);
475
477 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
478 false);
479 channel_respond(messenger,
480 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
481 TRUE);
482 g_main_loop_run(loop);
483}
484
485TEST(FlKeyboardManagerTest, EngineHandledChannelHandledAsync) {
486 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
487
488 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
489
490 g_autoptr(FlEngine) engine =
491 fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
492 g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
493
494 EXPECT_TRUE(fl_engine_start(engine, nullptr));
495
496 // Handle channel and embedder calls asynchronously.
497 g_autoptr(GPtrArray) channel_calls =
498 g_ptr_array_new_with_free_func(g_object_unref);
500 messenger, "flutter/keyevent",
501 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
502 gpointer user_data) {
503 GPtrArray* channel_calls = static_cast<GPtrArray*>(user_data);
504 g_ptr_array_add(channel_calls, g_object_ref(task));
505 // Will respond async
506 return static_cast<FlValue*>(nullptr);
507 },
508 channel_calls);
509 g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
510 reinterpret_cast<GDestroyNotify>(call_record_free));
512 SendKeyEvent, ([&embedder_call_records](
513 auto engine, const FlutterKeyEvent* event,
515 g_ptr_array_add(embedder_call_records,
517 return kSuccess;
518 }));
519
520 g_autoptr(FlKeyEvent) event = fl_key_event_new(
521 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
522 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
524 manager, event, nullptr,
525 [](GObject* object, GAsyncResult* result, gpointer user_data) {
526 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
528 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
529 EXPECT_EQ(redispatched_event, nullptr);
530 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
531 },
532 loop);
533
534 EXPECT_EQ(embedder_call_records->len, 1u);
535 EXPECT_EQ(channel_calls->len, 1u);
536
538 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
539 true);
540 channel_respond(messenger,
541 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
542 TRUE);
543 g_main_loop_run(loop);
544}
545
546TEST(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledAsync) {
547 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
548
549 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
550
551 g_autoptr(FlEngine) engine =
552 fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
553 g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
554
555 EXPECT_TRUE(fl_engine_start(engine, nullptr));
556
557 // Handle channel and embedder calls asynchronously.
558 g_autoptr(GPtrArray) channel_calls =
559 g_ptr_array_new_with_free_func(g_object_unref);
561 messenger, "flutter/keyevent",
562 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
563 gpointer user_data) {
564 GPtrArray* channel_calls = static_cast<GPtrArray*>(user_data);
565 g_ptr_array_add(channel_calls, g_object_ref(task));
566 // Will respond async
567 return static_cast<FlValue*>(nullptr);
568 },
569 channel_calls);
570 g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
571 reinterpret_cast<GDestroyNotify>(call_record_free));
573 SendKeyEvent, ([&embedder_call_records](
574 auto engine, const FlutterKeyEvent* event,
576 g_ptr_array_add(embedder_call_records,
578 return kSuccess;
579 }));
580
581 g_autoptr(FlKeyEvent) event = fl_key_event_new(
582 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
583 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
585 manager, event, nullptr,
586 [](GObject* object, GAsyncResult* result, gpointer user_data) {
587 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
589 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
590 EXPECT_NE(redispatched_event, nullptr);
591 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
592 },
593 loop);
594
595 EXPECT_EQ(embedder_call_records->len, 1u);
596 EXPECT_EQ(channel_calls->len, 1u);
597
599 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
600 false);
601 channel_respond(messenger,
602 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
603 FALSE);
604 g_main_loop_run(loop);
605}
606
607TEST(FlKeyboardManagerTest, CorrectLogicalKeyForLayouts) {
608 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
609
610 g_autoptr(FlDartProject) project = fl_dart_project_new();
611 g_autoptr(FlEngine) engine = fl_engine_new(project);
612 g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
613
614 EXPECT_TRUE(fl_engine_start(engine, nullptr));
615
616 g_autoptr(GPtrArray) call_records = g_ptr_array_new_with_free_func(
617 reinterpret_cast<GDestroyNotify>(call_record_free));
619 SendKeyEvent,
620 ([&call_records](auto engine, const FlutterKeyEvent* event,
622 g_ptr_array_add(call_records,
624 return kSuccess;
625 }));
626
627 auto sendTap = [&](guint8 keycode, guint keyval, guint8 group) {
628 g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
629 0, TRUE, keycode, keyval, static_cast<GdkModifierType>(0), group);
630 fl_keyboard_manager_handle_event(manager, event1, nullptr, nullptr,
631 nullptr);
632 g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
633 0, FALSE, keycode, keyval, static_cast<GdkModifierType>(0), group);
634 fl_keyboard_manager_handle_event(manager, event2, nullptr, nullptr,
635 nullptr);
636 };
637
638 /* US keyboard layout */
639
640 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
641 VERIFY_DOWN(kLogicalKeyA, "a");
642
643 sendTap(kKeyCodeKeyA, GDK_KEY_A, 0); // Shift-KeyA
644 VERIFY_DOWN(kLogicalKeyA, "A");
645
646 sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
647 VERIFY_DOWN(kLogicalDigit1, "1");
648
649 sendTap(kKeyCodeDigit1, GDK_KEY_exclam, 0); // Shift-Digit1
650 VERIFY_DOWN(kLogicalDigit1, "!");
651
652 sendTap(kKeyCodeMinus, GDK_KEY_minus, 0); // Minus
653 VERIFY_DOWN(kLogicalMinus, "-");
654
655 sendTap(kKeyCodeMinus, GDK_KEY_underscore, 0); // Shift-Minus
656 VERIFY_DOWN(kLogicalUnderscore, "_");
657
658 /* French keyboard layout, group 3, which is when the input method is showing
659 * "Fr" */
660
662 manager,
663 [](const GdkKeymapKey* key, gpointer user_data) {
664 MockLayoutData* layout_data = static_cast<MockLayoutData*>(user_data);
665 guint8 group = static_cast<guint8>(key->group);
666 EXPECT_LT(group, layout_data->size());
667 const MockGroupLayoutData* group_layout = (*layout_data)[group];
668 EXPECT_NE(group_layout, nullptr);
669 EXPECT_TRUE(key->level == 0 || key->level == 1);
670 bool shift = key->level == 1;
671 return (*group_layout)[key->keycode * 2 + shift];
672 },
673 reinterpret_cast<gpointer>(const_cast<MockLayoutData*>(&kLayoutFrench)));
674
675 sendTap(kKeyCodeKeyA, GDK_KEY_q, 3); // KeyA
676 VERIFY_DOWN(kLogicalKeyQ, "q");
677
678 sendTap(kKeyCodeKeyA, GDK_KEY_Q, 3); // Shift-KeyA
679 VERIFY_DOWN(kLogicalKeyQ, "Q");
680
681 sendTap(kKeyCodeSemicolon, GDK_KEY_m, 3); // ; but prints M
682 VERIFY_DOWN(kLogicalKeyM, "m");
683
684 sendTap(kKeyCodeKeyM, GDK_KEY_comma, 3); // M but prints ,
685 VERIFY_DOWN(kLogicalComma, ",");
686
687 sendTap(kKeyCodeDigit1, GDK_KEY_ampersand, 3); // Digit1
688 VERIFY_DOWN(kLogicalDigit1, "&");
689
690 sendTap(kKeyCodeDigit1, GDK_KEY_1, 3); // Shift-Digit1
691 VERIFY_DOWN(kLogicalDigit1, "1");
692
693 sendTap(kKeyCodeMinus, GDK_KEY_parenright, 3); // Minus
694 VERIFY_DOWN(kLogicalParenthesisRight, ")");
695
696 sendTap(kKeyCodeMinus, GDK_KEY_degree, 3); // Shift-Minus
697 VERIFY_DOWN(static_cast<uint32_t>(L'°'), "°");
698
699 /* French keyboard layout, group 0, which is pressing the "extra key for
700 * triggering input method" key once after switching to French IME. */
701
702 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
703 VERIFY_DOWN(kLogicalKeyA, "a");
704
705 sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
706 VERIFY_DOWN(kLogicalDigit1, "1");
707
708 /* Russian keyboard layout, group 2 */
710 manager,
711 [](const GdkKeymapKey* key, gpointer user_data) {
712 MockLayoutData* layout_data = static_cast<MockLayoutData*>(user_data);
713 guint8 group = static_cast<guint8>(key->group);
714 EXPECT_LT(group, layout_data->size());
715 const MockGroupLayoutData* group_layout = (*layout_data)[group];
716 EXPECT_NE(group_layout, nullptr);
717 EXPECT_TRUE(key->level == 0 || key->level == 1);
718 bool shift = key->level == 1;
719 return (*group_layout)[key->keycode * 2 + shift];
720 },
721 reinterpret_cast<gpointer>(const_cast<MockLayoutData*>(&kLayoutRussian)));
722
723 sendTap(kKeyCodeKeyA, GDK_KEY_Cyrillic_ef, 2); // KeyA
724 VERIFY_DOWN(kLogicalKeyA, "Ñ„");
725
726 sendTap(kKeyCodeDigit1, GDK_KEY_1, 2); // Shift-Digit1
727 VERIFY_DOWN(kLogicalDigit1, "1");
728
729 sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_Cyrillic_ha, 2);
730 VERIFY_DOWN(kLogicalBracketLeft, "Ñ…");
731
732 /* Russian keyboard layout, group 0 */
733 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
734 VERIFY_DOWN(kLogicalKeyA, "a");
735
736 sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_bracketleft, 0);
737 VERIFY_DOWN(kLogicalBracketLeft, "[");
738}
739
740TEST(FlKeyboardManagerTest, SynthesizeModifiersIfNeeded) {
741 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
742
743 g_autoptr(FlDartProject) project = fl_dart_project_new();
744 g_autoptr(FlEngine) engine = fl_engine_new(project);
745 g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
746
747 EXPECT_TRUE(fl_engine_start(engine, nullptr));
748
749 g_autoptr(GPtrArray) call_records = g_ptr_array_new_with_free_func(
750 reinterpret_cast<GDestroyNotify>(call_record_free));
752 SendKeyEvent,
753 ([&call_records](auto engine, const FlutterKeyEvent* event,
755 g_ptr_array_add(call_records,
757 return kSuccess;
758 }));
759
760 auto verifyModifierIsSynthesized = [&](GdkModifierType mask,
761 uint64_t physical, uint64_t logical) {
762 // Modifier is pressed.
763 guint state = mask;
765 EXPECT_EQ(call_records->len, 1u);
766 CallRecord* record =
767 static_cast<CallRecord*>(g_ptr_array_index(call_records, 0));
768 EXPECT_EQ(record->event_type, kFlutterKeyEventTypeDown);
769 EXPECT_EQ(record->event_physical, physical);
770 EXPECT_EQ(record->event_logical, logical);
771 EXPECT_STREQ(record->event_character, NULL);
772 EXPECT_EQ(record->event_synthesized, true);
773 // Modifier is released.
774 state = state ^ mask;
776 EXPECT_EQ(call_records->len, 2u);
777 record = static_cast<CallRecord*>(g_ptr_array_index(call_records, 1));
778 EXPECT_EQ(record->event_type, kFlutterKeyEventTypeUp);
779 EXPECT_EQ(record->event_physical, physical);
780 EXPECT_EQ(record->event_logical, logical);
781 EXPECT_STREQ(record->event_character, NULL);
782 EXPECT_EQ(record->event_synthesized, true);
783 g_ptr_array_set_size(call_records, 0);
784 };
785
786 // No modifiers pressed.
787 guint state = 0;
789 EXPECT_EQ(call_records->len, 0u);
790 g_ptr_array_set_size(call_records, 0);
791
792 // Press and release each modifier once.
793 verifyModifierIsSynthesized(GDK_CONTROL_MASK, kPhysicalControlLeft,
794 kLogicalControlLeft);
795 verifyModifierIsSynthesized(GDK_META_MASK, kPhysicalMetaLeft,
796 kLogicalMetaLeft);
797 verifyModifierIsSynthesized(GDK_MOD1_MASK, kPhysicalAltLeft, kLogicalAltLeft);
798 verifyModifierIsSynthesized(GDK_SHIFT_MASK, kPhysicalShiftLeft,
799 kLogicalShiftLeft);
800}
801
802TEST(FlKeyboardManagerTest, GetPressedState) {
803 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
804
805 g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
806 g_autoptr(FlEngine) engine =
807 fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
808 g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
809
810 EXPECT_TRUE(fl_engine_start(engine, nullptr));
811
812 // Dispatch a key event.
814 messenger, "flutter/keyevent",
815 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
816 gpointer user_data) {
817 FlValue* response = fl_value_new_map();
818 fl_value_set_string_take(response, "handled", fl_value_new_bool(FALSE));
819 return response;
820 },
821 nullptr);
823 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
825 callback(false, user_data);
826 return kSuccess;
827 }));
828 g_autoptr(FlKeyEvent) event = fl_key_event_new(
829 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
830 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
832 manager, event, nullptr,
833 [](GObject* object, GAsyncResult* result, gpointer user_data) {
834 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
836 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
837 EXPECT_NE(redispatched_event, nullptr);
838 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
839 },
840 loop);
841 g_main_loop_run(loop);
842
843 GHashTable* pressed_state = fl_keyboard_manager_get_pressed_state(manager);
844 EXPECT_EQ(g_hash_table_size(pressed_state), 1u);
845
846 gpointer physical_key =
847 g_hash_table_lookup(pressed_state, uint64_to_gpointer(kPhysicalKeyA));
848 EXPECT_EQ(gpointer_to_uint64(physical_key), kLogicalKeyA);
849}
850
851// The following layout data is generated using DEBUG_PRINT_LAYOUT.
852
853const MockGroupLayoutData kLayoutRussian0{
854 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
855 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
856 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
857 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
858 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
859 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
860 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
861 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
862 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
863 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
864 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
865 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
866 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
867 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
868 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
869 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
870 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
871 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
872 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
873 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
874 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
875 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
876 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
877 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
878 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
879 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
880 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
881 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
882 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
883 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
884 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
885 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
886 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
887};
888
889const MockGroupLayoutData kLayoutRussian2{{
890 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
891 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
892 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
893 0xffff, 0x0031, 0x0021, 0x0000, 0x0031, 0x0021, 0x0032, 0x0022, // 0x08
894 0x0033, 0x06b0, 0x0034, 0x003b, 0x0035, 0x0025, 0x0036, 0x003a, // 0x0c
895 0x0037, 0x003f, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
896 0x002d, 0x005f, 0x003d, 0x002b, 0x0071, 0x0051, 0x0000, 0x0000, // 0x14
897 0x06ca, 0x06ea, 0x06c3, 0x06e3, 0x06d5, 0x06f5, 0x06cb, 0x06eb, // 0x18
898 0x06c5, 0x06e5, 0x06ce, 0x06ee, 0x06c7, 0x06e7, 0x06db, 0x06fb, // 0x1c
899 0x06dd, 0x06fd, 0x06da, 0x06fa, 0x06c8, 0x06e8, 0x06df, 0x06ff, // 0x20
900 0x0061, 0x0041, 0x0041, 0x0000, 0x06c6, 0x06e6, 0x06d9, 0x06f9, // 0x24
901 0x06d7, 0x06f7, 0x06c1, 0x06e1, 0x06d0, 0x06f0, 0x06d2, 0x06f2, // 0x28
902 0x06cf, 0x06ef, 0x06cc, 0x06ec, 0x06c4, 0x06e4, 0x06d6, 0x06f6, // 0x2c
903 0x06dc, 0x06fc, 0x06a3, 0x06b3, 0x007c, 0x0000, 0x005c, 0x002f, // 0x30
904 0x06d1, 0x06f1, 0x06de, 0x06fe, 0x06d3, 0x06f3, 0x06cd, 0x06ed, // 0x34
905 0x06c9, 0x06e9, 0x06d4, 0x06f4, 0x06d8, 0x06f8, 0x06c2, 0x06e2, // 0x38
906 0x06c0, 0x06e0, 0x002e, 0x002c, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
907 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
908 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
909 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
910 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
911 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
912 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
913 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
914 0xffff, 0xffff, 0x003c, 0x003e, 0x002f, 0x007c, 0xffff, 0xffff, // 0x5c
915 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
916 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x64
917 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0x0000, // 0x68
918 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
919 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
920 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
921 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, // 0x78
922 0x00b1, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x7c
923}};
924
925const MockGroupLayoutData kLayoutFrench0 = {
926 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
927 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
928 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
929 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
930 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
931 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
932 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
933 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
934 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
935 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
936 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
937 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
938 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
939 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
940 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
941 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
942 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
943 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
944 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
945 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
946 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
947 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
948 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
949 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
950 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
951 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
952 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
953 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
954 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
955 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
956 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
957 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
958 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
959};
960
961const MockGroupLayoutData kLayoutFrench3 = {
962 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
963 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
964 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
965 0x0000, 0xffff, 0x0000, 0x0000, 0x0026, 0x0031, 0x00e9, 0x0032, // 0x08
966 0x0022, 0x0033, 0x0027, 0x0034, 0x0028, 0x0035, 0x002d, 0x0036, // 0x0c
967 0x00e8, 0x0037, 0x005f, 0x0038, 0x00e7, 0x0039, 0x00e0, 0x0030, // 0x10
968 0x0029, 0x00b0, 0x003d, 0x002b, 0x0000, 0x0000, 0x0061, 0x0041, // 0x14
969 0x0061, 0x0041, 0x007a, 0x005a, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
970 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
971 0x006f, 0x004f, 0x0070, 0x0050, 0xffff, 0xffff, 0x0024, 0x00a3, // 0x20
972 0x0041, 0x0000, 0x0000, 0x0000, 0x0071, 0x0051, 0x0073, 0x0053, // 0x24
973 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
974 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x006d, 0x004d, // 0x2c
975 0x00f9, 0x0025, 0x00b2, 0x007e, 0x0000, 0x0000, 0x002a, 0x00b5, // 0x30
976 0x0077, 0x0057, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
977 0x0062, 0x0042, 0x006e, 0x004e, 0x002c, 0x003f, 0x003b, 0x002e, // 0x38
978 0x003a, 0x002f, 0x0021, 0x00a7, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
979 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
980 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
981 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
982 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
983 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
984 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
985 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
986 0xffff, 0x003c, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
987 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
988 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
989 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
990 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
991 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
992 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
993 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, // 0x78
994 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x7c
995};
996
997const MockLayoutData kLayoutRussian{&kLayoutRussian0, nullptr,
998 &kLayoutRussian2};
999const MockLayoutData kLayoutFrench{&kLayoutFrench0, nullptr, nullptr,
1000 &kLayoutFrench3};
1001
1002} // namespace
TEST(AsciiTableTest, Simple)
int32_t value
@ kSuccess
Definition embedder.h:73
void(* FlutterKeyEventCallback)(bool, void *)
Definition embedder.h:1427
FlutterKeyEventType
Definition embedder.h:1346
@ kFlutterKeyEventTypeUp
Definition embedder.h:1347
FlutterEngine engine
Definition main.cc:84
g_autoptr(GMutexLocker) locker
return TRUE
G_MODULE_EXPORT FlDartProject * fl_dart_project_new()
FlEngine * fl_engine_new_with_binary_messenger(FlBinaryMessenger *binary_messenger)
Definition fl_engine.cc:701
G_MODULE_EXPORT FlEngine * fl_engine_new(FlDartProject *project)
Definition fl_engine.cc:697
FlutterEngineProcTable * fl_engine_get_embedder_api(FlEngine *self)
Definition fl_engine.cc:868
gboolean fl_engine_start(FlEngine *self, GError **error)
Definition fl_engine.cc:726
FlKeyEvent * fl_key_event_new(guint32 time, gboolean is_press, guint16 keycode, guint keyval, GdkModifierType state, guint8 group)
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_BEGIN_DECLS GBytes * message
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
gpointer uint64_to_gpointer(uint64_t number)
Definition key_mapping.h:17
uint64_t gpointer_to_uint64(gpointer pointer)
Definition key_mapping.h:13
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:3717
const char * character
Definition embedder.h:1409

Function Documentation

◆ call_record_free()

static void call_record_free ( CallRecord record)
static

Definition at line 58 of file fl_keyboard_manager_test.cc.

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

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 44 of file fl_keyboard_manager_test.cc.

46 {
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}

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 63 of file fl_keyboard_manager_test.cc.

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

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