Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
fl_keyboard_manager_test.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <cstring>
8#include <vector>
9
16
18#include "gtest/gtest.h"
19
20#define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR) \
21 EXPECT_EQ(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)) \
22 ->event_type, \
23 kFlutterKeyEventTypeDown); \
24 EXPECT_EQ(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)) \
25 ->event_logical, \
26 (OUT_LOGICAL)); \
27 EXPECT_STREQ(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)) \
28 ->event_character, \
29 (OUT_CHAR)); \
30 EXPECT_EQ(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)) \
31 ->event_synthesized, \
32 false); \
33 g_ptr_array_set_size(call_records, 0)
34
44
47 void* callback_user_data) {
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}
58
59static void call_record_free(CallRecord* record) {
60 g_free(record->event_character);
61 g_free(record);
62}
63
64static void call_record_respond(CallRecord* record, bool handled) {
65 if (record->callback != nullptr) {
66 record->callback(handled, record->callback_user_data);
67 }
68}
69
70namespace {
71using ::flutter::testing::keycodes::kLogicalAltLeft;
72using ::flutter::testing::keycodes::kLogicalBracketLeft;
73using ::flutter::testing::keycodes::kLogicalComma;
74using ::flutter::testing::keycodes::kLogicalControlLeft;
75using ::flutter::testing::keycodes::kLogicalDigit1;
76using ::flutter::testing::keycodes::kLogicalKeyA;
77using ::flutter::testing::keycodes::kLogicalKeyB;
78using ::flutter::testing::keycodes::kLogicalKeyM;
79using ::flutter::testing::keycodes::kLogicalKeyQ;
80using ::flutter::testing::keycodes::kLogicalMetaLeft;
81using ::flutter::testing::keycodes::kLogicalMinus;
82using ::flutter::testing::keycodes::kLogicalParenthesisRight;
83using ::flutter::testing::keycodes::kLogicalSemicolon;
84using ::flutter::testing::keycodes::kLogicalShiftLeft;
85using ::flutter::testing::keycodes::kLogicalUnderscore;
86
87using ::flutter::testing::keycodes::kPhysicalAltLeft;
88using ::flutter::testing::keycodes::kPhysicalControlLeft;
89using ::flutter::testing::keycodes::kPhysicalKeyA;
90using ::flutter::testing::keycodes::kPhysicalKeyB;
91using ::flutter::testing::keycodes::kPhysicalMetaLeft;
92using ::flutter::testing::keycodes::kPhysicalShiftLeft;
93
94constexpr guint16 kKeyCodeKeyA = 0x26u;
95constexpr guint16 kKeyCodeKeyM = 0x3au;
96constexpr guint16 kKeyCodeDigit1 = 0x0au;
97constexpr guint16 kKeyCodeMinus = 0x14u;
98constexpr guint16 kKeyCodeSemicolon = 0x2fu;
99constexpr guint16 kKeyCodeKeyLeftBracket = 0x22u;
100
101// All key clues for a keyboard layout.
102//
103// The index is (keyCode * 2 + hasShift), where each value is the character for
104// this key (GTK only supports UTF-16.) Since the maximum keycode of interest
105// is 128, it has a total of 256 entries..
106typedef std::array<uint32_t, 256> MockGroupLayoutData;
107typedef std::vector<const MockGroupLayoutData*> MockLayoutData;
108
109extern const MockLayoutData kLayoutRussian;
110extern const MockLayoutData kLayoutFrench;
111
112class FlKeyboardManagerTest : public flutter::testing::LinuxTest {
113 protected:
114 void SetUp() override {
115 messenger = fl_mock_binary_messenger_new();
116 g_clear_object(&engine);
117 engine =
118 fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
120 StartEngine();
121 }
122
123 ~FlKeyboardManagerTest() {
124 g_clear_object(&manager);
125 g_clear_object(&messenger);
126 }
127
128 FlMockBinaryMessenger* messenger = nullptr;
129 FlKeyboardManager* manager = nullptr;
130 ::testing::NiceMock<flutter::testing::MockGtk> mock_gtk;
131};
132
133TEST_F(FlKeyboardManagerTest, EngineNoResponseChannelHandled) {
134 // Channel handles all events.
136 messenger, "flutter/keyevent",
137 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
138 gpointer user_data) {
139 g_autoptr(FlValue) return_value = fl_value_new_map();
140 fl_value_set_string_take(return_value, "handled",
142 return fl_value_ref(return_value);
143 },
144 nullptr);
145
146 // Don't handle first event - async call never completes.
148 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
150 void* user_data) { return kSuccess; }));
151 g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
152 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
153 gboolean first_event_completed = FALSE;
155 manager, event1, nullptr,
156 [](GObject* object, GAsyncResult* result, gpointer user_data) {
157 gboolean* first_event_completed = static_cast<gboolean*>(user_data);
158 *first_event_completed = TRUE;
159 },
160 &first_event_completed);
161
162 // Handle second event.
164 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
166 callback(true, user_data);
167 return kSuccess;
168 }));
169 g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
170 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
172 manager, event2, nullptr,
173 [](GObject* object, GAsyncResult* result, gpointer user_data) {
174 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
176 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
177 EXPECT_EQ(redispatched_event, nullptr);
178 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
179 },
180 loop);
181 g_main_loop_run(loop);
182
183 EXPECT_FALSE(first_event_completed);
184
185 // Passes if the cleanup does not crash.
186}
187
188TEST_F(FlKeyboardManagerTest, EngineHandledChannelNotHandledSync) {
189 // Handle channel and embedder calls synchronously.
191 messenger, "flutter/keyevent",
192 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
193 gpointer user_data) {
194 g_autoptr(FlValue) return_value = fl_value_new_map();
195 fl_value_set_string_take(return_value, "handled",
196 fl_value_new_bool(FALSE));
197 return fl_value_ref(return_value);
198 },
199 nullptr);
201 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
203 callback(true, user_data);
204 return kSuccess;
205 }));
206
207 g_autoptr(FlKeyEvent) event = fl_key_event_new(
208 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
210 manager, event, nullptr,
211 [](GObject* object, GAsyncResult* result, gpointer user_data) {
212 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
214 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
215 EXPECT_EQ(redispatched_event, nullptr);
216 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
217 },
218 loop);
219 g_main_loop_run(loop);
220}
221
222TEST_F(FlKeyboardManagerTest, EngineNotHandledChannelHandledSync) {
223 // Handle channel and embedder calls synchronously.
225 messenger, "flutter/keyevent",
226 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
227 gpointer user_data) {
228 g_autoptr(FlValue) return_value = fl_value_new_map();
229 fl_value_set_string_take(return_value, "handled",
231 return fl_value_ref(return_value);
232 },
233 nullptr);
235 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
237 callback(false, user_data);
238 return kSuccess;
239 }));
240
241 g_autoptr(FlKeyEvent) event = fl_key_event_new(
242 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
244 manager, event, nullptr,
245 [](GObject* object, GAsyncResult* result, gpointer user_data) {
246 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
248 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
249 EXPECT_EQ(redispatched_event, nullptr);
250 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
251 },
252 loop);
253 g_main_loop_run(loop);
254}
255
256TEST_F(FlKeyboardManagerTest, EngineHandledChannelHandledSync) {
257 // Handle channel and embedder calls synchronously.
259 messenger, "flutter/keyevent",
260 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
261 gpointer user_data) {
262 g_autoptr(FlValue) return_value = fl_value_new_map();
263 fl_value_set_string_take(return_value, "handled",
265 return fl_value_ref(return_value);
266 },
267 nullptr);
269 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
271 callback(true, user_data);
272 return kSuccess;
273 }));
274
275 g_autoptr(FlKeyEvent) event = fl_key_event_new(
276 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
278 manager, event, nullptr,
279 [](GObject* object, GAsyncResult* result, gpointer user_data) {
280 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
282 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
283 EXPECT_EQ(redispatched_event, nullptr);
284 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
285 },
286 loop);
287 g_main_loop_run(loop);
288}
289
290TEST_F(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledSync) {
291 // Handle channel and embedder calls synchronously.
293 messenger, "flutter/keyevent",
294 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
295 gpointer user_data) {
296 g_autoptr(FlValue) return_value = fl_value_new_map();
297 fl_value_set_string_take(return_value, "handled",
298 fl_value_new_bool(FALSE));
299 return fl_value_ref(return_value);
300 },
301 nullptr);
303 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
305 callback(false, user_data);
306 return kSuccess;
307 }));
308
309 g_autoptr(FlKeyEvent) event = fl_key_event_new(
310 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
312 manager, event, nullptr,
313 [](GObject* object, GAsyncResult* result, gpointer user_data) {
314 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
316 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
317 EXPECT_NE(redispatched_event, nullptr);
318 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
319 },
320 loop);
321 g_main_loop_run(loop);
322}
323
324static void channel_respond(FlMockBinaryMessenger* messenger,
325 GTask* task,
326 gboolean handled) {
328 fl_value_set_string_take(value, "handled", fl_value_new_bool(handled));
330}
331
332TEST_F(FlKeyboardManagerTest, EngineHandledChannelNotHandledAsync) {
333 // Handle channel and embedder calls asynchronously.
334 g_autoptr(GPtrArray) channel_calls =
335 g_ptr_array_new_with_free_func(g_object_unref);
337 messenger, "flutter/keyevent",
338 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
339 gpointer user_data) {
340 GPtrArray* channel_calls = static_cast<GPtrArray*>(user_data);
341 g_ptr_array_add(channel_calls, g_object_ref(task));
342 // Will respond async
343 return static_cast<FlValue*>(nullptr);
344 },
345 channel_calls);
346 g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
347 reinterpret_cast<GDestroyNotify>(call_record_free));
349 SendKeyEvent, ([&embedder_call_records](
350 auto engine, const FlutterKeyEvent* event,
352 g_ptr_array_add(embedder_call_records,
354 return kSuccess;
355 }));
356
357 g_autoptr(FlKeyEvent) event = fl_key_event_new(
358 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
360 manager, event, nullptr,
361 [](GObject* object, GAsyncResult* result, gpointer user_data) {
362 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
364 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
365 EXPECT_EQ(redispatched_event, nullptr);
366 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
367 },
368 loop);
369
370 EXPECT_EQ(embedder_call_records->len, 1u);
371 EXPECT_EQ(channel_calls->len, 1u);
372
374 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
375 true);
376 channel_respond(messenger,
377 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
378 FALSE);
379 g_main_loop_run(loop);
380}
381
382TEST_F(FlKeyboardManagerTest, EngineNotHandledChannelHandledAsync) {
383 // Handle channel and embedder calls asynchronously.
384 g_autoptr(GPtrArray) channel_calls =
385 g_ptr_array_new_with_free_func(g_object_unref);
387 messenger, "flutter/keyevent",
388 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
389 gpointer user_data) {
390 GPtrArray* channel_calls = static_cast<GPtrArray*>(user_data);
391 g_ptr_array_add(channel_calls, g_object_ref(task));
392 // Will respond async
393 return static_cast<FlValue*>(nullptr);
394 },
395 channel_calls);
396 g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
397 reinterpret_cast<GDestroyNotify>(call_record_free));
399 SendKeyEvent, ([&embedder_call_records](
400 auto engine, const FlutterKeyEvent* event,
402 g_ptr_array_add(embedder_call_records,
404 return kSuccess;
405 }));
406
407 g_autoptr(FlKeyEvent) event = fl_key_event_new(
408 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
410 manager, event, nullptr,
411 [](GObject* object, GAsyncResult* result, gpointer user_data) {
412 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
414 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
415 EXPECT_EQ(redispatched_event, nullptr);
416 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
417 },
418 loop);
419
420 EXPECT_EQ(embedder_call_records->len, 1u);
421 EXPECT_EQ(channel_calls->len, 1u);
422
424 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
425 false);
426 channel_respond(messenger,
427 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
428 TRUE);
429 g_main_loop_run(loop);
430}
431
432TEST_F(FlKeyboardManagerTest, EngineHandledChannelHandledAsync) {
433 // Handle channel and embedder calls asynchronously.
434 g_autoptr(GPtrArray) channel_calls =
435 g_ptr_array_new_with_free_func(g_object_unref);
437 messenger, "flutter/keyevent",
438 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
439 gpointer user_data) {
440 GPtrArray* channel_calls = static_cast<GPtrArray*>(user_data);
441 g_ptr_array_add(channel_calls, g_object_ref(task));
442 // Will respond async
443 return static_cast<FlValue*>(nullptr);
444 },
445 channel_calls);
446 g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
447 reinterpret_cast<GDestroyNotify>(call_record_free));
449 SendKeyEvent, ([&embedder_call_records](
450 auto engine, const FlutterKeyEvent* event,
452 g_ptr_array_add(embedder_call_records,
454 return kSuccess;
455 }));
456
457 g_autoptr(FlKeyEvent) event = fl_key_event_new(
458 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
460 manager, event, nullptr,
461 [](GObject* object, GAsyncResult* result, gpointer user_data) {
462 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
464 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
465 EXPECT_EQ(redispatched_event, nullptr);
466 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
467 },
468 loop);
469
470 EXPECT_EQ(embedder_call_records->len, 1u);
471 EXPECT_EQ(channel_calls->len, 1u);
472
474 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
475 true);
476 channel_respond(messenger,
477 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
478 TRUE);
479 g_main_loop_run(loop);
480}
481
482TEST_F(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledAsync) {
483 // Handle channel and embedder calls asynchronously.
484 g_autoptr(GPtrArray) channel_calls =
485 g_ptr_array_new_with_free_func(g_object_unref);
487 messenger, "flutter/keyevent",
488 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
489 gpointer user_data) {
490 GPtrArray* channel_calls = static_cast<GPtrArray*>(user_data);
491 g_ptr_array_add(channel_calls, g_object_ref(task));
492 // Will respond async
493 return static_cast<FlValue*>(nullptr);
494 },
495 channel_calls);
496 g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
497 reinterpret_cast<GDestroyNotify>(call_record_free));
499 SendKeyEvent, ([&embedder_call_records](
500 auto engine, const FlutterKeyEvent* event,
502 g_ptr_array_add(embedder_call_records,
504 return kSuccess;
505 }));
506
507 g_autoptr(FlKeyEvent) event = fl_key_event_new(
508 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
510 manager, event, nullptr,
511 [](GObject* object, GAsyncResult* result, gpointer user_data) {
512 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
514 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
515 EXPECT_NE(redispatched_event, nullptr);
516 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
517 },
518 loop);
519
520 EXPECT_EQ(embedder_call_records->len, 1u);
521 EXPECT_EQ(channel_calls->len, 1u);
522
524 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
525 false);
526 channel_respond(messenger,
527 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
528 FALSE);
529 g_main_loop_run(loop);
530}
531
532TEST_F(FlKeyboardManagerTest, CorrectLogicalKeyForLayouts) {
533 g_autoptr(GPtrArray) call_records = g_ptr_array_new_with_free_func(
534 reinterpret_cast<GDestroyNotify>(call_record_free));
536 SendKeyEvent,
537 ([&call_records](auto engine, const FlutterKeyEvent* event,
539 g_ptr_array_add(call_records,
541 return kSuccess;
542 }));
543
544 // Respond to the key event channel so its async messages complete instead of
545 // leaking their GTasks.
547 messenger, "flutter/keyevent",
548 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
549 gpointer user_data) {
550 g_autoptr(FlValue) return_value = fl_value_new_map();
551 fl_value_set_string_take(return_value, "handled",
552 fl_value_new_bool(FALSE));
553 return fl_value_ref(return_value);
554 },
555 nullptr);
556
557 auto sendTap = [&](guint8 keycode, guint keyval, guint8 group) {
558 g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
559 0, TRUE, keycode, keyval, static_cast<GdkModifierType>(0), group);
560 fl_keyboard_manager_handle_event(manager, event1, nullptr, nullptr,
561 nullptr);
562 g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
563 0, FALSE, keycode, keyval, static_cast<GdkModifierType>(0), group);
564 fl_keyboard_manager_handle_event(manager, event2, nullptr, nullptr,
565 nullptr);
566 };
567
568 /* US keyboard layout */
569
570 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
571 VERIFY_DOWN(kLogicalKeyA, "a");
572
573 sendTap(kKeyCodeKeyA, GDK_KEY_A, 0); // Shift-KeyA
574 VERIFY_DOWN(kLogicalKeyA, "A");
575
576 sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
577 VERIFY_DOWN(kLogicalDigit1, "1");
578
579 sendTap(kKeyCodeDigit1, GDK_KEY_exclam, 0); // Shift-Digit1
580 VERIFY_DOWN(kLogicalDigit1, "!");
581
582 sendTap(kKeyCodeMinus, GDK_KEY_minus, 0); // Minus
583 VERIFY_DOWN(kLogicalMinus, "-");
584
585 sendTap(kKeyCodeMinus, GDK_KEY_underscore, 0); // Shift-Minus
586 VERIFY_DOWN(kLogicalUnderscore, "_");
587
588 /* French keyboard layout, group 3, which is when the input method is showing
589 * "Fr" */
590
592 manager,
593 [](const GdkKeymapKey* key, gpointer user_data) {
594 MockLayoutData* layout_data = static_cast<MockLayoutData*>(user_data);
595 guint8 group = static_cast<guint8>(key->group);
596 EXPECT_LT(group, layout_data->size());
597 const MockGroupLayoutData* group_layout = (*layout_data)[group];
598 EXPECT_NE(group_layout, nullptr);
599 EXPECT_TRUE(key->level == 0 || key->level == 1);
600 bool shift = key->level == 1;
601 return (*group_layout)[key->keycode * 2 + shift];
602 },
603 reinterpret_cast<gpointer>(const_cast<MockLayoutData*>(&kLayoutFrench)));
604
605 sendTap(kKeyCodeKeyA, GDK_KEY_q, 3); // KeyA
606 VERIFY_DOWN(kLogicalKeyQ, "q");
607
608 sendTap(kKeyCodeKeyA, GDK_KEY_Q, 3); // Shift-KeyA
609 VERIFY_DOWN(kLogicalKeyQ, "Q");
610
611 sendTap(kKeyCodeSemicolon, GDK_KEY_m, 3); // ; but prints M
612 VERIFY_DOWN(kLogicalKeyM, "m");
613
614 sendTap(kKeyCodeKeyM, GDK_KEY_comma, 3); // M but prints ,
615 VERIFY_DOWN(kLogicalComma, ",");
616
617 sendTap(kKeyCodeDigit1, GDK_KEY_ampersand, 3); // Digit1
618 VERIFY_DOWN(kLogicalDigit1, "&");
619
620 sendTap(kKeyCodeDigit1, GDK_KEY_1, 3); // Shift-Digit1
621 VERIFY_DOWN(kLogicalDigit1, "1");
622
623 sendTap(kKeyCodeMinus, GDK_KEY_parenright, 3); // Minus
624 VERIFY_DOWN(kLogicalParenthesisRight, ")");
625
626 sendTap(kKeyCodeMinus, GDK_KEY_degree, 3); // Shift-Minus
627 VERIFY_DOWN(static_cast<uint32_t>(L'°'), "°");
628
629 /* French keyboard layout, group 0, which is pressing the "extra key for
630 * triggering input method" key once after switching to French IME. */
631
632 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
633 VERIFY_DOWN(kLogicalKeyA, "a");
634
635 sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
636 VERIFY_DOWN(kLogicalDigit1, "1");
637
638 /* Russian keyboard layout, group 2 */
640 manager,
641 [](const GdkKeymapKey* key, gpointer user_data) {
642 MockLayoutData* layout_data = static_cast<MockLayoutData*>(user_data);
643 guint8 group = static_cast<guint8>(key->group);
644 EXPECT_LT(group, layout_data->size());
645 const MockGroupLayoutData* group_layout = (*layout_data)[group];
646 EXPECT_NE(group_layout, nullptr);
647 EXPECT_TRUE(key->level == 0 || key->level == 1);
648 bool shift = key->level == 1;
649 return (*group_layout)[key->keycode * 2 + shift];
650 },
651 reinterpret_cast<gpointer>(const_cast<MockLayoutData*>(&kLayoutRussian)));
652
653 sendTap(kKeyCodeKeyA, GDK_KEY_Cyrillic_ef, 2); // KeyA
654 VERIFY_DOWN(kLogicalKeyA, "Ñ„");
655
656 sendTap(kKeyCodeDigit1, GDK_KEY_1, 2); // Shift-Digit1
657 VERIFY_DOWN(kLogicalDigit1, "1");
658
659 sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_Cyrillic_ha, 2);
660 VERIFY_DOWN(kLogicalBracketLeft, "Ñ…");
661
662 /* Russian keyboard layout, group 0 */
663 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
664 VERIFY_DOWN(kLogicalKeyA, "a");
665
666 sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_bracketleft, 0);
667 VERIFY_DOWN(kLogicalBracketLeft, "[");
668}
669
670TEST_F(FlKeyboardManagerTest, SynthesizeModifiersIfNeeded) {
671 g_autoptr(GPtrArray) call_records = g_ptr_array_new_with_free_func(
672 reinterpret_cast<GDestroyNotify>(call_record_free));
674 SendKeyEvent,
675 ([&call_records](auto engine, const FlutterKeyEvent* event,
677 g_ptr_array_add(call_records,
679 return kSuccess;
680 }));
681
682 auto verifyModifierIsSynthesized = [&](GdkModifierType mask,
683 uint64_t physical, uint64_t logical) {
684 // Modifier is pressed.
685 guint state = mask;
687 EXPECT_EQ(call_records->len, 1u);
688 CallRecord* record =
689 static_cast<CallRecord*>(g_ptr_array_index(call_records, 0));
690 EXPECT_EQ(record->event_type, kFlutterKeyEventTypeDown);
691 EXPECT_EQ(record->event_physical, physical);
692 EXPECT_EQ(record->event_logical, logical);
693 EXPECT_STREQ(record->event_character, NULL);
694 EXPECT_EQ(record->event_synthesized, true);
695 // Modifier is released.
696 state = state ^ mask;
698 EXPECT_EQ(call_records->len, 2u);
699 record = static_cast<CallRecord*>(g_ptr_array_index(call_records, 1));
700 EXPECT_EQ(record->event_type, kFlutterKeyEventTypeUp);
701 EXPECT_EQ(record->event_physical, physical);
702 EXPECT_EQ(record->event_logical, logical);
703 EXPECT_STREQ(record->event_character, NULL);
704 EXPECT_EQ(record->event_synthesized, true);
705 g_ptr_array_set_size(call_records, 0);
706 };
707
708 // No modifiers pressed.
709 guint state = 0;
711 EXPECT_EQ(call_records->len, 0u);
712 g_ptr_array_set_size(call_records, 0);
713
714 // Press and release each modifier once.
715 verifyModifierIsSynthesized(GDK_CONTROL_MASK, kPhysicalControlLeft,
716 kLogicalControlLeft);
717 verifyModifierIsSynthesized(GDK_META_MASK, kPhysicalMetaLeft,
718 kLogicalMetaLeft);
719 verifyModifierIsSynthesized(GDK_MOD1_MASK, kPhysicalAltLeft, kLogicalAltLeft);
720 verifyModifierIsSynthesized(GDK_SHIFT_MASK, kPhysicalShiftLeft,
721 kLogicalShiftLeft);
722}
723
724TEST_F(FlKeyboardManagerTest, GetPressedState) {
725 // Dispatch a key event.
727 messenger, "flutter/keyevent",
728 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
729 gpointer user_data) {
730 FlValue* response = fl_value_new_map();
731 fl_value_set_string_take(response, "handled", fl_value_new_bool(FALSE));
732 return response;
733 },
734 nullptr);
736 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
738 callback(false, user_data);
739 return kSuccess;
740 }));
741 g_autoptr(FlKeyEvent) event = fl_key_event_new(
742 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
744 manager, event, nullptr,
745 [](GObject* object, GAsyncResult* result, gpointer user_data) {
746 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
748 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
749 EXPECT_NE(redispatched_event, nullptr);
750 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
751 },
752 loop);
753 g_main_loop_run(loop);
754
755 GHashTable* pressed_state = fl_keyboard_manager_get_pressed_state(manager);
756 EXPECT_EQ(g_hash_table_size(pressed_state), 1u);
757
758 gpointer physical_key =
759 g_hash_table_lookup(pressed_state, uint64_to_gpointer(kPhysicalKeyA));
760 EXPECT_EQ(gpointer_to_uint64(physical_key), kLogicalKeyA);
761}
762
763// The following layout data is generated using DEBUG_PRINT_LAYOUT.
764
765const MockGroupLayoutData kLayoutRussian0{
766 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
767 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
768 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
769 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
770 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
771 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
772 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
773 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
774 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
775 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
776 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
777 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
778 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
779 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
780 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
781 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
782 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
783 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
784 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
785 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
786 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
787 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
788 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
789 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
790 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
791 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
792 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
793 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
794 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
795 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
796 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
797 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
798 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
799};
800
801const MockGroupLayoutData kLayoutRussian2{{
802 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
803 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
804 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
805 0xffff, 0x0031, 0x0021, 0x0000, 0x0031, 0x0021, 0x0032, 0x0022, // 0x08
806 0x0033, 0x06b0, 0x0034, 0x003b, 0x0035, 0x0025, 0x0036, 0x003a, // 0x0c
807 0x0037, 0x003f, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
808 0x002d, 0x005f, 0x003d, 0x002b, 0x0071, 0x0051, 0x0000, 0x0000, // 0x14
809 0x06ca, 0x06ea, 0x06c3, 0x06e3, 0x06d5, 0x06f5, 0x06cb, 0x06eb, // 0x18
810 0x06c5, 0x06e5, 0x06ce, 0x06ee, 0x06c7, 0x06e7, 0x06db, 0x06fb, // 0x1c
811 0x06dd, 0x06fd, 0x06da, 0x06fa, 0x06c8, 0x06e8, 0x06df, 0x06ff, // 0x20
812 0x0061, 0x0041, 0x0041, 0x0000, 0x06c6, 0x06e6, 0x06d9, 0x06f9, // 0x24
813 0x06d7, 0x06f7, 0x06c1, 0x06e1, 0x06d0, 0x06f0, 0x06d2, 0x06f2, // 0x28
814 0x06cf, 0x06ef, 0x06cc, 0x06ec, 0x06c4, 0x06e4, 0x06d6, 0x06f6, // 0x2c
815 0x06dc, 0x06fc, 0x06a3, 0x06b3, 0x007c, 0x0000, 0x005c, 0x002f, // 0x30
816 0x06d1, 0x06f1, 0x06de, 0x06fe, 0x06d3, 0x06f3, 0x06cd, 0x06ed, // 0x34
817 0x06c9, 0x06e9, 0x06d4, 0x06f4, 0x06d8, 0x06f8, 0x06c2, 0x06e2, // 0x38
818 0x06c0, 0x06e0, 0x002e, 0x002c, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
819 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
820 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
821 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
822 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
823 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
824 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
825 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
826 0xffff, 0xffff, 0x003c, 0x003e, 0x002f, 0x007c, 0xffff, 0xffff, // 0x5c
827 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
828 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x64
829 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0x0000, // 0x68
830 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
831 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
832 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
833 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, // 0x78
834 0x00b1, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x7c
835}};
836
837const MockGroupLayoutData kLayoutFrench0 = {
838 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
839 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
840 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
841 0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040, // 0x08
842 0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e, // 0x0c
843 0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029, // 0x10
844 0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff, // 0x14
845 0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
846 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
847 0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d, // 0x20
848 0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053, // 0x24
849 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
850 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a, // 0x2c
851 0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c, // 0x30
852 0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
853 0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c, // 0x38
854 0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
855 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
856 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
857 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
858 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
859 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
860 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
861 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
862 0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
863 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
864 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
865 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
866 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
867 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
868 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
869 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x78
870 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff, // 0x7c
871};
872
873const MockGroupLayoutData kLayoutFrench3 = {
874 // +0x0 Shift +0x1 Shift +0x2 Shift +0x3 Shift
875 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x00
876 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, // 0x04
877 0x0000, 0xffff, 0x0000, 0x0000, 0x0026, 0x0031, 0x00e9, 0x0032, // 0x08
878 0x0022, 0x0033, 0x0027, 0x0034, 0x0028, 0x0035, 0x002d, 0x0036, // 0x0c
879 0x00e8, 0x0037, 0x005f, 0x0038, 0x00e7, 0x0039, 0x00e0, 0x0030, // 0x10
880 0x0029, 0x00b0, 0x003d, 0x002b, 0x0000, 0x0000, 0x0061, 0x0041, // 0x14
881 0x0061, 0x0041, 0x007a, 0x005a, 0x0065, 0x0045, 0x0072, 0x0052, // 0x18
882 0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049, // 0x1c
883 0x006f, 0x004f, 0x0070, 0x0050, 0xffff, 0xffff, 0x0024, 0x00a3, // 0x20
884 0x0041, 0x0000, 0x0000, 0x0000, 0x0071, 0x0051, 0x0073, 0x0053, // 0x24
885 0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048, // 0x28
886 0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x006d, 0x004d, // 0x2c
887 0x00f9, 0x0025, 0x00b2, 0x007e, 0x0000, 0x0000, 0x002a, 0x00b5, // 0x30
888 0x0077, 0x0057, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056, // 0x34
889 0x0062, 0x0042, 0x006e, 0x004e, 0x002c, 0x003f, 0x003b, 0x002e, // 0x38
890 0x003a, 0x002f, 0x0021, 0x00a7, 0xffff, 0xffff, 0xffff, 0xffff, // 0x3c
891 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x40
892 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x44
893 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x48
894 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x4c
895 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x50
896 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x54
897 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x58
898 0xffff, 0x003c, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff, // 0x5c
899 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x60
900 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, // 0x64
901 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x68
902 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x6c
903 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x70
904 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x74
905 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, // 0x78
906 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 0x7c
907};
908
909const MockLayoutData kLayoutRussian{&kLayoutRussian0, nullptr,
910 &kLayoutRussian2};
911const MockLayoutData kLayoutFrench{&kLayoutFrench0, nullptr, nullptr,
912 &kLayoutFrench3};
913
914} // 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
@ kFlutterKeyEventTypeDown
Definition embedder.h:1403
@ 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