10 #include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
17 #include "flutter/shell/platform/linux/testing/fl_test.h"
18 #include "flutter/shell/platform/linux/testing/mock_binary_messenger.h"
19 #include "flutter/shell/platform/linux/testing/mock_text_input_plugin.h"
20 #include "flutter/testing/testing.h"
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
28 #define EXPECT_KEY_EVENT(RECORD, TYPE, PHYSICAL, LOGICAL, CHAR, SYNTHESIZED) \
29 EXPECT_EQ((RECORD).type, CallRecord::kKeyCallEmbedder); \
30 EXPECT_EQ((RECORD).event->type, (TYPE)); \
31 EXPECT_EQ((RECORD).event->physical, (PHYSICAL)); \
32 EXPECT_EQ((RECORD).event->logical, (LOGICAL)); \
33 EXPECT_STREQ((RECORD).event->character, (CHAR)); \
34 EXPECT_EQ((RECORD).event->synthesized, (SYNTHESIZED));
36 #define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR) \
37 EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder); \
38 EXPECT_EQ(call_records[0].event->type, kFlutterKeyEventTypeDown); \
39 EXPECT_EQ(call_records[0].event->logical, (OUT_LOGICAL)); \
40 EXPECT_STREQ(call_records[0].event->character, (OUT_CHAR)); \
41 EXPECT_EQ(call_records[0].event->synthesized, false); \
45 using ::flutter::testing::keycodes::kLogicalAltLeft;
46 using ::flutter::testing::keycodes::kLogicalBracketLeft;
47 using ::flutter::testing::keycodes::kLogicalComma;
48 using ::flutter::testing::keycodes::kLogicalControlLeft;
49 using ::flutter::testing::keycodes::kLogicalDigit1;
50 using ::flutter::testing::keycodes::kLogicalKeyA;
51 using ::flutter::testing::keycodes::kLogicalKeyB;
52 using ::flutter::testing::keycodes::kLogicalKeyM;
53 using ::flutter::testing::keycodes::kLogicalKeyQ;
54 using ::flutter::testing::keycodes::kLogicalMetaLeft;
55 using ::flutter::testing::keycodes::kLogicalMinus;
56 using ::flutter::testing::keycodes::kLogicalParenthesisRight;
57 using ::flutter::testing::keycodes::kLogicalSemicolon;
58 using ::flutter::testing::keycodes::kLogicalShiftLeft;
59 using ::flutter::testing::keycodes::kLogicalUnderscore;
61 using ::flutter::testing::keycodes::kPhysicalAltLeft;
62 using ::flutter::testing::keycodes::kPhysicalControlLeft;
63 using ::flutter::testing::keycodes::kPhysicalKeyA;
64 using ::flutter::testing::keycodes::kPhysicalKeyB;
65 using ::flutter::testing::keycodes::kPhysicalMetaLeft;
66 using ::flutter::testing::keycodes::kPhysicalShiftLeft;
69 typedef std::function<void(
bool handled)> AsyncKeyCallback;
70 typedef std::function<void(AsyncKeyCallback
callback)> ChannelCallHandler;
71 typedef std::function<void(
const FlutterKeyEvent*
event,
74 typedef std::function<void(std::unique_ptr<FlKeyEvent>)> RedispatchHandler;
87 std::unique_ptr<FlutterKeyEvent>
event;
88 std::unique_ptr<char[]> event_character;
94 char* cloneString(
const char* source) {
95 if (source ==
nullptr) {
98 size_t charLen = strlen(source);
99 char*
target =
new char[charLen + 1];
100 strncpy(
target, source, charLen + 1);
104 constexpr guint16 kKeyCodeKeyA = 0x26u;
105 constexpr guint16 kKeyCodeKeyB = 0x38u;
106 constexpr guint16 kKeyCodeKeyM = 0x3au;
107 constexpr guint16 kKeyCodeDigit1 = 0x0au;
108 constexpr guint16 kKeyCodeMinus = 0x14u;
109 constexpr guint16 kKeyCodeSemicolon = 0x2fu;
110 constexpr guint16 kKeyCodeKeyLeftBracket = 0x22u;
112 static constexpr
char kKeyEventChannelName[] =
"flutter/keyevent";
113 static constexpr
char kKeyboardChannelName[] =
"flutter/keyboard";
115 static constexpr uint64_t kMockPhysicalKey = 42;
116 static constexpr uint64_t kMockLogicalKey = 42;
123 typedef std::array<uint32_t, 256> MockGroupLayoutData;
124 typedef std::vector<const MockGroupLayoutData*> MockLayoutData;
126 extern const MockLayoutData kLayoutUs;
127 extern const MockLayoutData kLayoutRussian;
128 extern const MockLayoutData kLayoutFrench;
133 fl_mock_view_delegate,
139 fl_mock_key_binary_messenger,
141 MOCK_KEY_BINARY_MESSENGER,
148 g_autoptr(FlMethodResponse) response =
155 *result_listener << ::testing::PrintToString(response);
164 struct _FlMockKeyBinaryMessenger {
165 GObject parent_instance;
167 ChannelCallHandler callback_handler;
170 static void fl_mock_key_binary_messenger_iface_init(
171 FlBinaryMessengerInterface* iface);
174 FlMockKeyBinaryMessenger,
175 fl_mock_key_binary_messenger,
177 G_IMPLEMENT_INTERFACE(fl_binary_messenger_get_type(),
178 fl_mock_key_binary_messenger_iface_init))
180 static void fl_mock_key_binary_messenger_class_init(
181 FlMockKeyBinaryMessengerClass* klass) {}
183 static void fl_mock_key_binary_messenger_send_on_channel(
184 FlBinaryMessenger* messenger,
185 const gchar* channel,
187 GCancellable* cancellable,
190 FlMockKeyBinaryMessenger*
self = FL_MOCK_KEY_BINARY_MESSENGER(messenger);
193 EXPECT_STREQ(channel, kKeyEventChannelName);
194 self->callback_handler([
self, cancellable,
callback,
196 g_autoptr(GTask) task =
201 g_autoptr(GError)
error =
nullptr;
205 g_task_return_pointer(task, data,
206 reinterpret_cast<GDestroyNotify
>(g_bytes_unref));
211 static GBytes* fl_mock_key_binary_messenger_send_on_channel_finish(
212 FlBinaryMessenger* messenger,
215 return static_cast<GBytes*
>(g_task_propagate_pointer(G_TASK(
result),
error));
218 static void fl_mock_binary_messenger_resize_channel(
219 FlBinaryMessenger* messenger,
220 const gchar* channel,
225 static void fl_mock_binary_messenger_set_allow_channel_overflow(
226 FlBinaryMessenger* messenger,
227 const gchar* channel,
232 static void fl_mock_key_binary_messenger_iface_init(
233 FlBinaryMessengerInterface* iface) {
234 iface->set_message_handler_on_channel =
235 [](FlBinaryMessenger* messenger,
const gchar* channel,
237 GDestroyNotify destroy_notify) {
238 EXPECT_STREQ(channel, kKeyEventChannelName);
242 iface->send_response = [](FlBinaryMessenger* messenger,
243 FlBinaryMessengerResponseHandle* response_handle,
244 GBytes* response, GError**
error) -> gboolean {
247 g_return_val_if_reached(
TRUE);
250 iface->send_on_channel = fl_mock_key_binary_messenger_send_on_channel;
251 iface->send_on_channel_finish =
252 fl_mock_key_binary_messenger_send_on_channel_finish;
253 iface->resize_channel = fl_mock_binary_messenger_resize_channel;
254 iface->set_allow_channel_overflow =
255 fl_mock_binary_messenger_set_allow_channel_overflow;
258 static void fl_mock_key_binary_messenger_init(FlMockKeyBinaryMessenger*
self) {}
260 static FlMockKeyBinaryMessenger* fl_mock_key_binary_messenger_new() {
261 FlMockKeyBinaryMessenger*
self = FL_MOCK_KEY_BINARY_MESSENGER(
262 g_object_new(fl_mock_key_binary_messenger_get_type(), NULL));
265 FL_IS_MOCK_KEY_BINARY_MESSENGER(
self);
270 static void fl_mock_key_binary_messenger_set_callback_handler(
271 FlMockKeyBinaryMessenger*
self,
272 ChannelCallHandler handler) {
273 self->callback_handler = std::move(handler);
278 struct _FlMockViewDelegate {
279 GObject parent_instance;
281 FlMockKeyBinaryMessenger* messenger;
282 EmbedderCallHandler embedder_handler;
283 bool text_filter_result;
284 RedispatchHandler redispatch_handler;
286 const MockLayoutData* layout_data;
289 static void fl_mock_view_keyboard_delegate_iface_init(
290 FlKeyboardViewDelegateInterface* iface);
294 fl_mock_view_delegate,
296 G_IMPLEMENT_INTERFACE(fl_keyboard_view_delegate_get_type(),
297 fl_mock_view_keyboard_delegate_iface_init))
299 static void fl_mock_view_delegate_init(FlMockViewDelegate*
self) {}
301 static void fl_mock_view_delegate_dispose(GObject*
object) {
302 FlMockViewDelegate*
self = FL_MOCK_VIEW_DELEGATE(
object);
304 g_clear_object(&
self->messenger);
306 G_OBJECT_CLASS(fl_mock_view_delegate_parent_class)->dispose(
object);
309 static void fl_mock_view_delegate_class_init(FlMockViewDelegateClass* klass) {
310 G_OBJECT_CLASS(klass)->dispose = fl_mock_view_delegate_dispose;
315 static void fl_mock_view_keyboard_send_key_event(
316 FlKeyboardViewDelegate* view_delegate,
317 const FlutterKeyEvent*
event,
320 FlMockViewDelegate*
self = FL_MOCK_VIEW_DELEGATE(view_delegate);
328 static gboolean fl_mock_view_keyboard_text_filter_key_press(
329 FlKeyboardViewDelegate* view_delegate,
331 FlMockViewDelegate*
self = FL_MOCK_VIEW_DELEGATE(view_delegate);
332 return self->text_filter_result;
335 static FlBinaryMessenger* fl_mock_view_keyboard_get_messenger(
336 FlKeyboardViewDelegate* view_delegate) {
337 FlMockViewDelegate*
self = FL_MOCK_VIEW_DELEGATE(view_delegate);
338 return FL_BINARY_MESSENGER(
self->messenger);
341 static void fl_mock_view_keyboard_redispatch_event(
342 FlKeyboardViewDelegate* view_delegate,
343 std::unique_ptr<FlKeyEvent>
event) {
344 FlMockViewDelegate*
self = FL_MOCK_VIEW_DELEGATE(view_delegate);
345 if (
self->redispatch_handler) {
346 self->redispatch_handler(std::move(
event));
350 static void fl_mock_view_keyboard_subscribe_to_layout_change(
351 FlKeyboardViewDelegate* delegate,
353 FlMockViewDelegate*
self = FL_MOCK_VIEW_DELEGATE(delegate);
354 self->layout_notifier = std::move(notifier);
357 static guint fl_mock_view_keyboard_lookup_key(FlKeyboardViewDelegate* delegate,
358 const GdkKeymapKey* key) {
359 FlMockViewDelegate*
self = FL_MOCK_VIEW_DELEGATE(delegate);
360 guint8 group =
static_cast<guint8
>(key->group);
361 EXPECT_LT(group,
self->layout_data->size());
362 const MockGroupLayoutData* group_layout = (*
self->layout_data)[group];
363 EXPECT_TRUE(group_layout !=
nullptr);
364 EXPECT_TRUE(key->level == 0 || key->level == 1);
365 bool shift = key->level == 1;
366 return (*group_layout)[key->keycode * 2 + shift];
369 static GHashTable* fl_mock_view_keyboard_get_keyboard_state(
370 FlKeyboardViewDelegate* view_delegate) {
371 GHashTable*
result = g_hash_table_new(g_direct_hash, g_direct_equal);
372 g_hash_table_insert(
result,
reinterpret_cast<gpointer
>(kMockPhysicalKey),
373 reinterpret_cast<gpointer
>(kMockLogicalKey));
378 static void fl_mock_view_keyboard_delegate_iface_init(
379 FlKeyboardViewDelegateInterface* iface) {
380 iface->send_key_event = fl_mock_view_keyboard_send_key_event;
381 iface->text_filter_key_press = fl_mock_view_keyboard_text_filter_key_press;
382 iface->get_messenger = fl_mock_view_keyboard_get_messenger;
383 iface->redispatch_event = fl_mock_view_keyboard_redispatch_event;
384 iface->subscribe_to_layout_change =
385 fl_mock_view_keyboard_subscribe_to_layout_change;
386 iface->lookup_key = fl_mock_view_keyboard_lookup_key;
387 iface->get_keyboard_state = fl_mock_view_keyboard_get_keyboard_state;
390 static FlMockViewDelegate* fl_mock_view_delegate_new() {
391 FlMockViewDelegate*
self = FL_MOCK_VIEW_DELEGATE(
392 g_object_new(fl_mock_view_delegate_get_type(),
nullptr));
395 FL_IS_MOCK_VIEW_DELEGATE(
self);
397 self->messenger = fl_mock_key_binary_messenger_new();
402 static void fl_mock_view_set_embedder_handler(FlMockViewDelegate*
self,
403 EmbedderCallHandler handler) {
404 self->embedder_handler = std::move(handler);
407 static void fl_mock_view_set_text_filter_result(FlMockViewDelegate*
self,
409 self->text_filter_result =
result;
412 static void fl_mock_view_set_redispatch_handler(FlMockViewDelegate*
self,
413 RedispatchHandler handler) {
414 self->redispatch_handler = std::move(handler);
417 static void fl_mock_view_set_layout(FlMockViewDelegate*
self,
418 const MockLayoutData* layout) {
419 self->layout_data = layout;
420 if (
self->layout_notifier !=
nullptr) {
421 self->layout_notifier();
431 new_event->
origin =
nullptr;
444 gboolean is_modifier,
447 event->is_press = is_press;
449 event->state =
state;
450 event->keyval = keyval;
451 event->string =
nullptr;
452 event->group = group;
453 event->keycode = keycode;
455 event->origin = origin_event;
460 class KeyboardTester {
463 ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
465 view_ = fl_mock_view_delegate_new();
466 respondToEmbedderCallsWith(
false);
467 respondToChannelCallsWith(
false);
468 respondToTextInputWith(
false);
469 setLayout(kLayoutUs);
476 g_clear_object(&view_);
477 g_clear_object(&manager_);
484 void flushChannelMessages() {
485 GMainLoop* loop = g_main_loop_new(
nullptr, 0);
486 g_idle_add(_flushChannelMessagesCb, loop);
487 g_main_loop_run(loop);
496 int redispatchEventsAndClear(
497 std::vector<std::unique_ptr<FlKeyEvent>>& events) {
498 size_t event_count = events.size();
499 int first_error = -1;
500 during_redispatch_ =
true;
501 for (
size_t event_id = 0; event_id < event_count; event_id += 1) {
503 manager_, events[event_id].release());
504 EXPECT_FALSE(handled);
506 first_error = first_error == -1 ? event_id : first_error;
509 during_redispatch_ =
false;
511 return first_error < 0 ? event_count : -first_error;
514 void respondToEmbedderCallsWith(
bool response) {
515 fl_mock_view_set_embedder_handler(
516 view_, [response,
this](
const FlutterKeyEvent*
event,
518 EXPECT_FALSE(during_redispatch_);
523 void recordEmbedderCallsTo(std::vector<CallRecord>& storage) {
524 fl_mock_view_set_embedder_handler(
525 view_, [&storage,
this](
const FlutterKeyEvent*
event,
527 EXPECT_FALSE(during_redispatch_);
528 auto new_event = std::make_unique<FlutterKeyEvent>(*
event);
529 char* new_event_character = cloneString(
event->character);
530 new_event->character = new_event_character;
531 storage.push_back(CallRecord{
532 .type = CallRecord::kKeyCallEmbedder,
534 .event = std::move(new_event),
535 .event_character = std::unique_ptr<char[]>(new_event_character),
540 void respondToEmbedderCallsWithAndRecordsTo(
542 std::vector<CallRecord>& storage) {
543 fl_mock_view_set_embedder_handler(
544 view_, [&storage, response,
this](
const FlutterKeyEvent*
event,
546 EXPECT_FALSE(during_redispatch_);
547 auto new_event = std::make_unique<FlutterKeyEvent>(*
event);
548 char* new_event_character = cloneString(
event->character);
549 new_event->character = new_event_character;
550 storage.push_back(CallRecord{
551 .type = CallRecord::kKeyCallEmbedder,
552 .event = std::move(new_event),
553 .event_character = std::unique_ptr<char[]>(new_event_character),
559 void respondToChannelCallsWith(
bool response) {
560 fl_mock_key_binary_messenger_set_callback_handler(
561 view_->messenger, [response,
this](
const AsyncKeyCallback&
callback) {
562 EXPECT_FALSE(during_redispatch_);
567 void recordChannelCallsTo(std::vector<CallRecord>& storage) {
568 fl_mock_key_binary_messenger_set_callback_handler(
569 view_->messenger, [&storage,
this](AsyncKeyCallback
callback) {
570 EXPECT_FALSE(during_redispatch_);
571 storage.push_back(CallRecord{
572 .type = CallRecord::kKeyCallChannel,
573 .callback = std::move(callback),
578 void respondToTextInputWith(
bool response) {
579 fl_mock_view_set_text_filter_result(view_, response);
582 void recordRedispatchedEventsTo(
583 std::vector<std::unique_ptr<FlKeyEvent>>& storage) {
584 fl_mock_view_set_redispatch_handler(
585 view_, [&storage](std::unique_ptr<FlKeyEvent> key) {
586 storage.push_back(std::move(key));
590 void setLayout(
const MockLayoutData& layout) {
591 fl_mock_view_set_layout(view_, &layout);
595 FlMockViewDelegate* view_;
597 bool during_redispatch_ =
false;
599 static gboolean _flushChannelMessagesCb(gpointer data) {
600 g_autoptr(GMainLoop) loop =
reinterpret_cast<GMainLoop*
>(data);
601 g_main_loop_quit(loop);
608 TEST(FlKeyboardManagerTest, DisposeWithUnresolvedPends) {
609 KeyboardTester tester;
610 std::vector<CallRecord> call_records;
613 tester.recordEmbedderCallsTo(call_records);
618 tester.respondToEmbedderCallsWith(
true);
623 tester.flushChannelMessages();
628 TEST(FlKeyboardManagerTest, SingleDelegateWithAsyncResponds) {
629 KeyboardTester tester;
630 std::vector<CallRecord> call_records;
631 std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
633 gboolean manager_handled =
false;
636 tester.recordEmbedderCallsTo(call_records);
637 tester.recordRedispatchedEventsTo(redispatched);
643 tester.flushChannelMessages();
644 EXPECT_EQ(manager_handled,
true);
645 EXPECT_EQ(redispatched.size(), 0u);
646 EXPECT_EQ(call_records.size(), 1u);
648 kLogicalKeyA,
"a",
false);
650 call_records[0].callback(
true);
651 tester.flushChannelMessages();
652 EXPECT_EQ(redispatched.size(), 0u);
654 call_records.clear();
660 tester.flushChannelMessages();
661 EXPECT_EQ(manager_handled,
true);
662 EXPECT_EQ(redispatched.size(), 0u);
663 EXPECT_EQ(call_records.size(), 1u);
665 kLogicalKeyA,
nullptr,
false);
671 tester.flushChannelMessages();
672 EXPECT_EQ(manager_handled,
true);
673 EXPECT_EQ(redispatched.size(), 0u);
674 EXPECT_EQ(call_records.size(), 2u);
676 kLogicalKeyB,
"b",
false);
679 call_records[1].callback(
false);
680 EXPECT_EQ(redispatched.size(), 1u);
681 EXPECT_EQ(redispatched[0]->keyval, 0x62u);
682 call_records[0].callback(
false);
683 tester.flushChannelMessages();
684 EXPECT_EQ(redispatched.size(), 2u);
685 EXPECT_EQ(redispatched[1]->keyval, 0x61u);
688 call_records.clear();
691 EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 2);
692 tester.flushChannelMessages();
693 EXPECT_EQ(call_records.size(), 0u);
701 tester.flushChannelMessages();
702 EXPECT_EQ(manager_handled,
true);
703 EXPECT_EQ(redispatched.size(), 0u);
704 EXPECT_EQ(call_records.size(), 1u);
706 call_records[0].callback(
true);
710 TEST(FlKeyboardManagerTest, SingleDelegateWithSyncResponds) {
711 KeyboardTester tester;
712 gboolean manager_handled =
false;
713 std::vector<CallRecord> call_records;
714 std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
717 tester.respondToEmbedderCallsWithAndRecordsTo(
true, call_records);
718 tester.recordRedispatchedEventsTo(redispatched);
724 tester.flushChannelMessages();
725 EXPECT_EQ(manager_handled,
true);
726 EXPECT_EQ(call_records.size(), 1u);
728 kLogicalKeyA,
"a",
false);
729 EXPECT_EQ(redispatched.size(), 0u);
730 call_records.clear();
733 redispatched.clear();
736 tester.respondToEmbedderCallsWithAndRecordsTo(
false, call_records);
740 tester.flushChannelMessages();
741 EXPECT_EQ(manager_handled,
true);
742 EXPECT_EQ(call_records.size(), 1u);
744 kLogicalKeyA,
nullptr,
false);
745 EXPECT_EQ(redispatched.size(), 1u);
746 call_records.clear();
750 EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
751 EXPECT_EQ(call_records.size(), 0u);
756 TEST(FlKeyboardManagerTest, WithTwoAsyncDelegates) {
757 KeyboardTester tester;
758 std::vector<CallRecord> call_records;
759 std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
761 gboolean manager_handled =
false;
763 tester.recordEmbedderCallsTo(call_records);
764 tester.recordChannelCallsTo(call_records);
765 tester.recordRedispatchedEventsTo(redispatched);
773 EXPECT_EQ(manager_handled,
true);
774 EXPECT_EQ(redispatched.size(), 0u);
775 EXPECT_EQ(call_records.size(), 2u);
777 EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder);
778 EXPECT_EQ(call_records[1].type, CallRecord::kKeyCallChannel);
780 call_records[0].callback(
true);
781 call_records[1].callback(
false);
782 tester.flushChannelMessages();
783 EXPECT_EQ(redispatched.size(), 0u);
786 call_records.clear();
793 EXPECT_EQ(manager_handled,
true);
794 EXPECT_EQ(redispatched.size(), 0u);
795 EXPECT_EQ(call_records.size(), 2u);
797 EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder);
798 EXPECT_EQ(call_records[1].type, CallRecord::kKeyCallChannel);
800 call_records[0].callback(
false);
801 call_records[1].callback(
false);
803 call_records.clear();
806 tester.flushChannelMessages();
807 EXPECT_EQ(redispatched.size(), 1u);
808 EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
809 EXPECT_EQ(call_records.size(), 0u);
814 TEST(FlKeyboardManagerTest, TextInputPluginReturnsFalse) {
815 KeyboardTester tester;
816 std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
817 gboolean manager_handled =
false;
818 tester.recordRedispatchedEventsTo(redispatched);
819 tester.respondToTextInputWith(
false);
825 tester.flushChannelMessages();
826 EXPECT_EQ(manager_handled,
true);
828 EXPECT_EQ(redispatched.size(), 1u);
831 EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
836 TEST(FlKeyboardManagerTest, TextInputPluginReturnsTrue) {
837 KeyboardTester tester;
838 std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
839 gboolean manager_handled =
false;
840 tester.recordRedispatchedEventsTo(redispatched);
841 tester.respondToTextInputWith(
true);
847 tester.flushChannelMessages();
848 EXPECT_EQ(manager_handled,
true);
850 EXPECT_EQ(redispatched.size(), 0u);
855 TEST(FlKeyboardManagerTest, CorrectLogicalKeyForLayouts) {
856 KeyboardTester tester;
858 std::vector<CallRecord> call_records;
859 tester.recordEmbedderCallsTo(call_records);
861 auto sendTap = [&](guint8 keycode, guint keyval, guint8 group) {
872 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0);
875 sendTap(kKeyCodeKeyA, GDK_KEY_A, 0);
878 sendTap(kKeyCodeDigit1, GDK_KEY_1, 0);
881 sendTap(kKeyCodeDigit1, GDK_KEY_exclam, 0);
884 sendTap(kKeyCodeMinus, GDK_KEY_minus, 0);
887 sendTap(kKeyCodeMinus, GDK_KEY_underscore, 0);
893 tester.setLayout(kLayoutFrench);
895 sendTap(kKeyCodeKeyA, GDK_KEY_q, 3);
898 sendTap(kKeyCodeKeyA, GDK_KEY_Q, 3);
901 sendTap(kKeyCodeSemicolon, GDK_KEY_m, 3);
904 sendTap(kKeyCodeKeyM, GDK_KEY_comma, 3);
907 sendTap(kKeyCodeDigit1, GDK_KEY_ampersand, 3);
910 sendTap(kKeyCodeDigit1, GDK_KEY_1, 3);
913 sendTap(kKeyCodeMinus, GDK_KEY_parenright, 3);
916 sendTap(kKeyCodeMinus, GDK_KEY_degree, 3);
922 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0);
925 sendTap(kKeyCodeDigit1, GDK_KEY_1, 0);
929 tester.setLayout(kLayoutRussian);
931 sendTap(kKeyCodeKeyA, GDK_KEY_Cyrillic_ef, 2);
934 sendTap(kKeyCodeDigit1, GDK_KEY_1, 2);
937 sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_Cyrillic_ha, 2);
941 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0);
944 sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_bracketleft, 0);
948 TEST(FlKeyboardManagerTest, SynthesizeModifiersIfNeeded) {
949 KeyboardTester tester;
950 std::vector<CallRecord> call_records;
951 tester.recordEmbedderCallsTo(call_records);
953 auto verifyModifierIsSynthesized = [&](GdkModifierType mask,
954 uint64_t physical, uint64_t logical) {
958 EXPECT_EQ(call_records.size(), 1u);
960 logical, NULL,
true);
964 EXPECT_EQ(call_records.size(), 2u);
965 EXPECT_KEY_EVENT(call_records[1], kFlutterKeyEventTypeUp, physical, logical,
967 call_records.clear();
973 EXPECT_EQ(call_records.size(), 0u);
974 call_records.clear();
977 verifyModifierIsSynthesized(GDK_CONTROL_MASK, kPhysicalControlLeft,
978 kLogicalControlLeft);
979 verifyModifierIsSynthesized(GDK_META_MASK, kPhysicalMetaLeft,
981 verifyModifierIsSynthesized(GDK_MOD1_MASK, kPhysicalAltLeft, kLogicalAltLeft);
982 verifyModifierIsSynthesized(GDK_SHIFT_MASK, kPhysicalShiftLeft,
986 TEST(FlKeyboardManagerTest, GetPressedState) {
987 KeyboardTester tester;
988 tester.respondToTextInputWith(
true);
995 GHashTable* pressedState =
997 EXPECT_EQ(g_hash_table_size(pressedState), 1u);
999 gpointer physical_key =
1004 TEST(FlKeyboardPluginTest, KeyboardChannelGetPressedState) {
1005 ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
1008 messenger, FL_KEYBOARD_VIEW_DELEGATE(fl_mock_view_delegate_new()));
1009 EXPECT_NE(manager,
nullptr);
1018 EXPECT_CALL(messenger,
1020 ::testing::Eq<FlBinaryMessenger*>(messenger), ::testing::_,
1021 MethodSuccessResponse(response), ::testing::_))
1022 .WillOnce(::testing::Return(
true));
1024 messenger.ReceiveMessage(kKeyboardChannelName, message);
1029 const MockGroupLayoutData kLayoutUs0{{
1031 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
1032 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
1033 0xffff, 0x0031, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040,
1034 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e,
1035 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029,
1036 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff,
1037 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052,
1038 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049,
1039 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d,
1040 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053,
1041 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048,
1042 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a,
1043 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c,
1044 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056,
1045 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c,
1046 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff,
1047 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1048 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1049 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1050 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1051 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1052 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1053 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1054 0xffff, 0xffff, 0x003c, 0x003e, 0x003c, 0x003e, 0xffff, 0xffff,
1055 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1056 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1057 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1058 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1059 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1060 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1061 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1062 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff,
1065 const MockGroupLayoutData kLayoutRussian0{
1067 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
1068 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
1069 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040,
1070 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e,
1071 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029,
1072 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff,
1073 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052,
1074 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049,
1075 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d,
1076 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053,
1077 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048,
1078 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a,
1079 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c,
1080 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056,
1081 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c,
1082 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff,
1083 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1084 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1085 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1086 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1087 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1088 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1089 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1090 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff,
1091 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1092 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff,
1093 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1094 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1095 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1096 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1097 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1098 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff,
1101 const MockGroupLayoutData kLayoutRussian2{{
1103 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
1104 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
1105 0xffff, 0x0031, 0x0021, 0x0000, 0x0031, 0x0021, 0x0032, 0x0022,
1106 0x0033, 0x06b0, 0x0034, 0x003b, 0x0035, 0x0025, 0x0036, 0x003a,
1107 0x0037, 0x003f, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029,
1108 0x002d, 0x005f, 0x003d, 0x002b, 0x0071, 0x0051, 0x0000, 0x0000,
1109 0x06ca, 0x06ea, 0x06c3, 0x06e3, 0x06d5, 0x06f5, 0x06cb, 0x06eb,
1110 0x06c5, 0x06e5, 0x06ce, 0x06ee, 0x06c7, 0x06e7, 0x06db, 0x06fb,
1111 0x06dd, 0x06fd, 0x06da, 0x06fa, 0x06c8, 0x06e8, 0x06df, 0x06ff,
1112 0x0061, 0x0041, 0x0041, 0x0000, 0x06c6, 0x06e6, 0x06d9, 0x06f9,
1113 0x06d7, 0x06f7, 0x06c1, 0x06e1, 0x06d0, 0x06f0, 0x06d2, 0x06f2,
1114 0x06cf, 0x06ef, 0x06cc, 0x06ec, 0x06c4, 0x06e4, 0x06d6, 0x06f6,
1115 0x06dc, 0x06fc, 0x06a3, 0x06b3, 0x007c, 0x0000, 0x005c, 0x002f,
1116 0x06d1, 0x06f1, 0x06de, 0x06fe, 0x06d3, 0x06f3, 0x06cd, 0x06ed,
1117 0x06c9, 0x06e9, 0x06d4, 0x06f4, 0x06d8, 0x06f8, 0x06c2, 0x06e2,
1118 0x06c0, 0x06e0, 0x002e, 0x002c, 0xffff, 0xffff, 0xffff, 0xffff,
1119 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1120 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1121 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1122 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1123 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1124 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1125 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1126 0xffff, 0xffff, 0x003c, 0x003e, 0x002f, 0x007c, 0xffff, 0xffff,
1127 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1128 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1129 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0x0000,
1130 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1131 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1132 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1133 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1,
1134 0x00b1, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1137 const MockGroupLayoutData kLayoutFrench0 = {
1139 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
1140 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
1141 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040,
1142 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e,
1143 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029,
1144 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff,
1145 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052,
1146 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049,
1147 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d,
1148 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053,
1149 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048,
1150 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a,
1151 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c,
1152 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056,
1153 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c,
1154 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff,
1155 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1156 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1157 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1158 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1159 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1160 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1161 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1162 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff,
1163 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1164 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff,
1165 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1166 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1167 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1168 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1169 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1170 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff,
1173 const MockGroupLayoutData kLayoutFrench3 = {
1175 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
1176 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
1177 0x0000, 0xffff, 0x0000, 0x0000, 0x0026, 0x0031, 0x00e9, 0x0032,
1178 0x0022, 0x0033, 0x0027, 0x0034, 0x0028, 0x0035, 0x002d, 0x0036,
1179 0x00e8, 0x0037, 0x005f, 0x0038, 0x00e7, 0x0039, 0x00e0, 0x0030,
1180 0x0029, 0x00b0, 0x003d, 0x002b, 0x0000, 0x0000, 0x0061, 0x0041,
1181 0x0061, 0x0041, 0x007a, 0x005a, 0x0065, 0x0045, 0x0072, 0x0052,
1182 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049,
1183 0x006f, 0x004f, 0x0070, 0x0050, 0xffff, 0xffff, 0x0024, 0x00a3,
1184 0x0041, 0x0000, 0x0000, 0x0000, 0x0071, 0x0051, 0x0073, 0x0053,
1185 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048,
1186 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x006d, 0x004d,
1187 0x00f9, 0x0025, 0x00b2, 0x007e, 0x0000, 0x0000, 0x002a, 0x00b5,
1188 0x0077, 0x0057, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056,
1189 0x0062, 0x0042, 0x006e, 0x004e, 0x002c, 0x003f, 0x003b, 0x002e,
1190 0x003a, 0x002f, 0x0021, 0x00a7, 0xffff, 0xffff, 0xffff, 0xffff,
1191 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1192 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1193 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1194 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1195 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1196 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1197 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1198 0xffff, 0x003c, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff,
1199 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1200 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff,
1201 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1202 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1203 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1204 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1205 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff,
1206 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1209 const MockLayoutData kLayoutUs{&kLayoutUs0};
1210 const MockLayoutData kLayoutRussian{&kLayoutRussian0,
nullptr,
1212 const MockLayoutData kLayoutFrench{&kLayoutFrench0,
nullptr,
nullptr,