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