Flutter Linux Embedder
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 
10 #include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
11 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
14 #include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h"
15 #include "flutter/shell/platform/linux/testing/mock_gtk.h"
16 
17 #include "flutter/shell/platform/linux/testing/linux_test.h"
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 
35 typedef struct {
36  FlutterKeyEventType event_type;
37  uint64_t event_physical;
38  uint64_t event_logical;
41  FlutterKeyEventCallback callback;
43 } CallRecord;
44 
45 static CallRecord* call_record_new(const FlutterKeyEvent* event,
46  FlutterKeyEventCallback callback,
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 
59 static void call_record_free(CallRecord* record) {
60  g_free(record->event_character);
61  g_free(record);
62 }
63 
64 static void call_record_respond(CallRecord* record, bool handled) {
65  if (record->callback != nullptr) {
66  record->callback(handled, record->callback_user_data);
67  }
68 }
69 
70 namespace {
71 using ::flutter::testing::keycodes::kLogicalAltLeft;
72 using ::flutter::testing::keycodes::kLogicalBracketLeft;
73 using ::flutter::testing::keycodes::kLogicalComma;
74 using ::flutter::testing::keycodes::kLogicalControlLeft;
75 using ::flutter::testing::keycodes::kLogicalDigit1;
76 using ::flutter::testing::keycodes::kLogicalKeyA;
77 using ::flutter::testing::keycodes::kLogicalKeyB;
78 using ::flutter::testing::keycodes::kLogicalKeyM;
79 using ::flutter::testing::keycodes::kLogicalKeyQ;
80 using ::flutter::testing::keycodes::kLogicalMetaLeft;
81 using ::flutter::testing::keycodes::kLogicalMinus;
82 using ::flutter::testing::keycodes::kLogicalParenthesisRight;
83 using ::flutter::testing::keycodes::kLogicalSemicolon;
84 using ::flutter::testing::keycodes::kLogicalShiftLeft;
85 using ::flutter::testing::keycodes::kLogicalUnderscore;
86 
87 using ::flutter::testing::keycodes::kPhysicalAltLeft;
88 using ::flutter::testing::keycodes::kPhysicalControlLeft;
89 using ::flutter::testing::keycodes::kPhysicalKeyA;
90 using ::flutter::testing::keycodes::kPhysicalKeyB;
91 using ::flutter::testing::keycodes::kPhysicalMetaLeft;
92 using ::flutter::testing::keycodes::kPhysicalShiftLeft;
93 
94 constexpr guint16 kKeyCodeKeyA = 0x26u;
95 constexpr guint16 kKeyCodeKeyM = 0x3au;
96 constexpr guint16 kKeyCodeDigit1 = 0x0au;
97 constexpr guint16 kKeyCodeMinus = 0x14u;
98 constexpr guint16 kKeyCodeSemicolon = 0x2fu;
99 constexpr 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..
106 typedef std::array<uint32_t, 256> MockGroupLayoutData;
107 typedef std::vector<const MockGroupLayoutData*> MockLayoutData;
108 
109 extern const MockLayoutData kLayoutRussian;
110 extern const MockLayoutData kLayoutFrench;
111 
112 class 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));
119  manager = fl_keyboard_manager_new(engine);
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 
133 TEST_F(FlKeyboardManagerTest, EngineNoResponseChannelHandled) {
134  // Channel handles all events.
135  fl_mock_binary_messenger_set_json_message_channel(
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.
147  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
148  SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
149  FlutterKeyEventCallback callback,
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.
163  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
164  SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
165  FlutterKeyEventCallback callback, void* user_data) {
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 
188 TEST_F(FlKeyboardManagerTest, EngineHandledChannelNotHandledSync) {
189  // Handle channel and embedder calls synchronously.
190  fl_mock_binary_messenger_set_json_message_channel(
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);
200  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
201  SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
202  FlutterKeyEventCallback callback, void* user_data) {
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 
222 TEST_F(FlKeyboardManagerTest, EngineNotHandledChannelHandledSync) {
223  // Handle channel and embedder calls synchronously.
224  fl_mock_binary_messenger_set_json_message_channel(
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);
234  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
235  SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
236  FlutterKeyEventCallback callback, void* user_data) {
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 
256 TEST_F(FlKeyboardManagerTest, EngineHandledChannelHandledSync) {
257  // Handle channel and embedder calls synchronously.
258  fl_mock_binary_messenger_set_json_message_channel(
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);
268  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
269  SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
270  FlutterKeyEventCallback callback, void* user_data) {
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 
290 TEST_F(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledSync) {
291  // Handle channel and embedder calls synchronously.
292  fl_mock_binary_messenger_set_json_message_channel(
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);
302  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
303  SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
304  FlutterKeyEventCallback callback, void* user_data) {
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 
324 static void channel_respond(FlMockBinaryMessenger* messenger,
325  GTask* task,
326  gboolean handled) {
328  fl_value_set_string_take(value, "handled", fl_value_new_bool(handled));
329  fl_mock_binary_messenger_json_message_channel_respond(messenger, task, value);
330 }
331 
332 TEST_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);
336  fl_mock_binary_messenger_set_json_message_channel(
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));
348  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
349  SendKeyEvent, ([&embedder_call_records](
350  auto engine, const FlutterKeyEvent* event,
351  FlutterKeyEventCallback callback, void* user_data) {
352  g_ptr_array_add(embedder_call_records,
353  call_record_new(event, callback, user_data));
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 
382 TEST_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);
386  fl_mock_binary_messenger_set_json_message_channel(
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));
398  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
399  SendKeyEvent, ([&embedder_call_records](
400  auto engine, const FlutterKeyEvent* event,
401  FlutterKeyEventCallback callback, void* user_data) {
402  g_ptr_array_add(embedder_call_records,
403  call_record_new(event, callback, user_data));
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 
432 TEST_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);
436  fl_mock_binary_messenger_set_json_message_channel(
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));
448  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
449  SendKeyEvent, ([&embedder_call_records](
450  auto engine, const FlutterKeyEvent* event,
451  FlutterKeyEventCallback callback, void* user_data) {
452  g_ptr_array_add(embedder_call_records,
453  call_record_new(event, callback, user_data));
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 
482 TEST_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);
486  fl_mock_binary_messenger_set_json_message_channel(
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));
498  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
499  SendKeyEvent, ([&embedder_call_records](
500  auto engine, const FlutterKeyEvent* event,
501  FlutterKeyEventCallback callback, void* user_data) {
502  g_ptr_array_add(embedder_call_records,
503  call_record_new(event, callback, user_data));
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 
532 TEST_F(FlKeyboardManagerTest, CorrectLogicalKeyForLayouts) {
533  g_autoptr(GPtrArray) call_records = g_ptr_array_new_with_free_func(
534  reinterpret_cast<GDestroyNotify>(call_record_free));
535  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
536  SendKeyEvent,
537  ([&call_records](auto engine, const FlutterKeyEvent* event,
538  FlutterKeyEventCallback callback, void* user_data) {
539  g_ptr_array_add(call_records,
540  call_record_new(event, callback, user_data));
541  return kSuccess;
542  }));
543 
544  // Respond to the key event channel so its async messages complete instead of
545  // leaking their GTasks.
546  fl_mock_binary_messenger_set_json_message_channel(
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 
670 TEST_F(FlKeyboardManagerTest, SynthesizeModifiersIfNeeded) {
671  g_autoptr(GPtrArray) call_records = g_ptr_array_new_with_free_func(
672  reinterpret_cast<GDestroyNotify>(call_record_free));
673  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
674  SendKeyEvent,
675  ([&call_records](auto engine, const FlutterKeyEvent* event,
676  FlutterKeyEventCallback callback, void* user_data) {
677  g_ptr_array_add(call_records,
678  call_record_new(event, callback, user_data));
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;
686  fl_keyboard_manager_sync_modifier_if_needed(manager, state, 1000);
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;
697  fl_keyboard_manager_sync_modifier_if_needed(manager, state, 1001);
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;
710  fl_keyboard_manager_sync_modifier_if_needed(manager, state, 1000);
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 
724 TEST_F(FlKeyboardManagerTest, GetPressedState) {
725  // Dispatch a key event.
726  fl_mock_binary_messenger_set_json_message_channel(
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);
735  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
736  SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
737  FlutterKeyEventCallback callback, void* user_data) {
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 
765 const 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 
801 const 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 
837 const 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 
873 const 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 
909 const MockLayoutData kLayoutRussian{&kLayoutRussian0, nullptr,
910  &kLayoutRussian2};
911 const MockLayoutData kLayoutFrench{&kLayoutFrench0, nullptr, nullptr,
912  &kLayoutFrench3};
913 
914 } // namespace
const char * message
g_autoptr(FlEngine) engine
const char FlTextDirection FlAssertiveness gpointer user_data
TEST_F(FlAccessibilityHandlerTest, Announce)
return TRUE
FlutterEngineProcTable * fl_engine_get_embedder_api(FlEngine *self)
Definition: fl_engine.cc:940
FlEngine * fl_engine_new_with_binary_messenger(FlBinaryMessenger *binary_messenger)
Definition: fl_engine.cc:734
FlKeyEvent * fl_key_event_new(guint32 time, gboolean is_press, guint16 keycode, guint keyval, GdkModifierType state, guint8 group)
Definition: fl_key_event.cc:34
gpointer uint64_to_gpointer(uint64_t number)
uint64_t gpointer_to_uint64(gpointer pointer)
constexpr guint16 kKeyCodeKeyA
FlKeyboardManager * fl_keyboard_manager_new(FlEngine *engine)
void fl_keyboard_manager_handle_event(FlKeyboardManager *self, FlKeyEvent *event, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
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)
GHashTable * fl_keyboard_manager_get_pressed_state(FlKeyboardManager *self)
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)
uint8_t value
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
G_MODULE_EXPORT FlValue * fl_value_ref(FlValue *self)
Definition: fl_value.cc:394
G_MODULE_EXPORT FlValue * fl_value_new_map()
Definition: fl_value.cc:366
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition: fl_value.h:42
FlutterKeyEventCallback callback
FlutterKeyEventType event_type