Flutter Linux Embedder
fl_key_embedder_responder_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 "flutter/shell/platform/linux/testing/linux_test.h"
8 #include "gtest/gtest.h"
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"
13 #include "flutter/shell/platform/linux/testing/fl_test.h"
14 
15 namespace {
16 constexpr gboolean kRelease = FALSE;
17 constexpr gboolean kPress = TRUE;
18 
19 constexpr guint16 kKeyCodeDigit1 = 0x0au;
20 constexpr guint16 kKeyCodeKeyA = 0x26u;
21 constexpr guint16 kKeyCodeShiftLeft = 0x32u;
22 constexpr guint16 kKeyCodeShiftRight = 0x3Eu;
23 constexpr guint16 kKeyCodeAltLeft = 0x40u;
24 constexpr guint16 kKeyCodeAltRight = 0x6Cu;
25 constexpr guint16 kKeyCodeNumpad1 = 0x57u;
26 constexpr guint16 kKeyCodeNumLock = 0x4Du;
27 constexpr guint16 kKeyCodeCapsLock = 0x42u;
28 constexpr guint16 kKeyCodeControlLeft = 0x25u;
29 constexpr guint16 kKeyCodeControlRight = 0x69u;
30 
31 using namespace ::flutter::testing::keycodes;
32 } // namespace
33 
34 static void clear_records(GPtrArray* array) {
35  g_ptr_array_remove_range(array, 0, array->len);
36 }
37 
38 G_DECLARE_FINAL_TYPE(FlKeyEmbedderCallRecord,
39  fl_key_embedder_call_record,
40  FL,
41  KEY_EMBEDDER_CALL_RECORD,
42  GObject);
43 
45  GObject parent_instance;
46 
47  FlutterKeyEvent* event;
48  FlutterKeyEventCallback callback;
49  gpointer user_data;
50 };
51 
52 G_DEFINE_TYPE(FlKeyEmbedderCallRecord,
53  fl_key_embedder_call_record,
54  G_TYPE_OBJECT)
55 
56 static void fl_key_embedder_call_record_init(FlKeyEmbedderCallRecord* self) {}
57 
58 // Dispose method for FlKeyEmbedderCallRecord.
59 static void fl_key_embedder_call_record_dispose(GObject* object) {
60  g_return_if_fail(FL_IS_KEY_EMBEDDER_CALL_RECORD(object));
61 
62  FlKeyEmbedderCallRecord* self = FL_KEY_EMBEDDER_CALL_RECORD(object);
63  if (self->event != nullptr) {
64  g_free(const_cast<char*>(self->event->character));
65  g_free(self->event);
66  }
67  G_OBJECT_CLASS(fl_key_embedder_call_record_parent_class)->dispose(object);
68 }
69 
70 // Class Initialization method for FlKeyEmbedderCallRecord class.
72  FlKeyEmbedderCallRecordClass* klass) {
73  G_OBJECT_CLASS(klass)->dispose = fl_key_embedder_call_record_dispose;
74 }
75 
76 static FlKeyEmbedderCallRecord* fl_key_embedder_call_record_new(
77  const FlutterKeyEvent* event,
78  FlutterKeyEventCallback callback,
79  gpointer user_data) {
80  g_return_val_if_fail(event != nullptr, nullptr);
81 
82  FlKeyEmbedderCallRecord* self = FL_KEY_EMBEDDER_CALL_RECORD(
83  g_object_new(fl_key_embedder_call_record_get_type(), nullptr));
84 
85  FlutterKeyEvent* clone_event = g_new(FlutterKeyEvent, 1);
86  *clone_event = *event;
87  if (event->character != nullptr) {
88  size_t character_length = strlen(event->character);
89  char* clone_character = g_new(char, character_length + 1);
90  strncpy(clone_character, event->character, character_length + 1);
91  clone_event->character = clone_character;
92  }
93  self->event = clone_event;
94  self->callback = callback;
95  self->user_data = user_data;
96 
97  return self;
98 }
99 
100 static void invoke_record_callback(FlKeyEmbedderCallRecord* record,
101  bool expected_handled) {
102  g_return_if_fail(record->callback != nullptr);
103  record->callback(expected_handled, record->user_data);
104 }
105 
106 class FlKeyEmbedderResponderTest : public flutter::testing::LinuxTest {};
107 
108 // Basic key presses
110  StartEngine();
111 
112  g_autoptr(FlKeyEmbedderResponder) responder =
114 
115  g_autoptr(GPtrArray) call_records =
116  g_ptr_array_new_with_free_func(g_object_unref);
117  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
118  SendKeyEvent,
119  ([&call_records](auto engine, const FlutterKeyEvent* event,
120  FlutterKeyEventCallback callback, void* user_data) {
121  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
122  event, callback, user_data));
123  return kSuccess;
124  }));
125 
126  // On a QWERTY keyboard, press key Q (physically key A), and release.
127  // Key down
128  g_autoptr(FlKeyEvent) event1 =
129  fl_key_event_new(12345, kPress, kKeyCodeKeyA, GDK_KEY_a,
130  static_cast<GdkModifierType>(0), 0);
131  g_autoptr(GMainLoop) loop1 = g_main_loop_new(nullptr, 0);
133  responder, event1, 0, nullptr,
134  [](GObject* object, GAsyncResult* result, gpointer user_data) {
135  gboolean handled;
137  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
138  EXPECT_EQ(handled, TRUE);
139 
140  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
141  },
142  loop1);
143 
144  EXPECT_EQ(call_records->len, 1u);
145  FlKeyEmbedderCallRecord* record =
146  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
147  EXPECT_EQ(record->event->struct_size, sizeof(FlutterKeyEvent));
148  EXPECT_EQ(record->event->timestamp, 12345000);
149  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
150  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
151  EXPECT_EQ(record->event->logical, kLogicalKeyA);
152  EXPECT_STREQ(record->event->character, "a");
153  EXPECT_EQ(record->event->synthesized, false);
154 
155  invoke_record_callback(record, TRUE);
156  g_main_loop_run(loop1);
157  clear_records(call_records);
158 
159  // Key up
160  g_autoptr(FlKeyEvent) event2 =
161  fl_key_event_new(12346, kRelease, kKeyCodeKeyA, GDK_KEY_a,
162  static_cast<GdkModifierType>(0), 0);
163  g_autoptr(GMainLoop) loop2 = g_main_loop_new(nullptr, 0);
165  responder, event2, 0, nullptr,
166  [](GObject* object, GAsyncResult* result, gpointer user_data) {
167  gboolean handled;
169  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
170  EXPECT_EQ(handled, FALSE);
171 
172  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
173  },
174  loop2);
175 
176  EXPECT_EQ(call_records->len, 1u);
177  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
178  EXPECT_EQ(record->event->struct_size, sizeof(FlutterKeyEvent));
179  EXPECT_EQ(record->event->timestamp, 12346000);
180  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
181  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
182  EXPECT_EQ(record->event->logical, kLogicalKeyA);
183  EXPECT_STREQ(record->event->character, nullptr);
184  EXPECT_EQ(record->event->synthesized, false);
185 
186  invoke_record_callback(record, FALSE);
187  g_main_loop_run(loop2);
188  clear_records(call_records);
189 
190  // On an AZERTY keyboard, press key Q (physically key A), and release.
191  // Key down
192  g_autoptr(FlKeyEvent) event3 =
193  fl_key_event_new(12347, kPress, kKeyCodeKeyA, GDK_KEY_q,
194  static_cast<GdkModifierType>(0), 0);
195  g_autoptr(GMainLoop) loop3 = g_main_loop_new(nullptr, 0);
197  responder, event3, 0, nullptr,
198  [](GObject* object, GAsyncResult* result, gpointer user_data) {
199  gboolean handled;
201  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
202  EXPECT_EQ(handled, TRUE);
203 
204  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
205  },
206  loop3);
207 
208  EXPECT_EQ(call_records->len, 1u);
209  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
210  EXPECT_EQ(record->event->struct_size, sizeof(FlutterKeyEvent));
211  EXPECT_EQ(record->event->timestamp, 12347000);
212  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
213  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
214  EXPECT_EQ(record->event->logical, kLogicalKeyQ);
215  EXPECT_STREQ(record->event->character, "q");
216  EXPECT_EQ(record->event->synthesized, false);
217 
218  invoke_record_callback(record, TRUE);
219  g_main_loop_run(loop3);
220  clear_records(call_records);
221 
222  // Key up
223  g_autoptr(FlKeyEvent) event4 =
224  fl_key_event_new(12348, kRelease, kKeyCodeKeyA, GDK_KEY_q,
225  static_cast<GdkModifierType>(0), 0);
226  g_autoptr(GMainLoop) loop4 = g_main_loop_new(nullptr, 0);
228  responder, event4, 0, nullptr,
229  [](GObject* object, GAsyncResult* result, gpointer user_data) {
230  gboolean handled;
232  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
233  EXPECT_EQ(handled, FALSE);
234 
235  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
236  },
237  loop4);
238 
239  EXPECT_EQ(call_records->len, 1u);
240  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
241  EXPECT_EQ(record->event->struct_size, sizeof(FlutterKeyEvent));
242  EXPECT_EQ(record->event->timestamp, 12348000);
243  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
244  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
245  EXPECT_EQ(record->event->logical, kLogicalKeyQ);
246  EXPECT_STREQ(record->event->character, nullptr);
247  EXPECT_EQ(record->event->synthesized, false);
248 
249  invoke_record_callback(record, FALSE);
250  g_main_loop_run(loop4);
251 }
252 
253 // Basic key presses, but uses the specified logical key if it is not 0.
254 TEST_F(FlKeyEmbedderResponderTest, UsesSpecifiedLogicalKey) {
255  StartEngine();
256 
257  g_autoptr(FlKeyEmbedderResponder) responder =
259 
260  g_autoptr(GPtrArray) call_records =
261  g_ptr_array_new_with_free_func(g_object_unref);
262  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
263  SendKeyEvent,
264  ([&call_records](auto engine, const FlutterKeyEvent* event,
265  FlutterKeyEventCallback callback, void* user_data) {
266  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
267  event, callback, user_data));
268  return kSuccess;
269  }));
270 
271  // On an AZERTY keyboard, press physical key 1, and release.
272  // Key down
273  g_autoptr(FlKeyEvent) event =
274  fl_key_event_new(12345, kPress, kKeyCodeDigit1, GDK_KEY_ampersand,
275  static_cast<GdkModifierType>(0), 0);
276  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
278  responder, event, kLogicalDigit1, nullptr,
279  [](GObject* object, GAsyncResult* result, gpointer user_data) {
280  gboolean handled;
282  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
283  EXPECT_EQ(handled, TRUE);
284 
285  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
286  },
287  loop);
288 
289  EXPECT_EQ(call_records->len, 1u);
290  FlKeyEmbedderCallRecord* record =
291  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
292  EXPECT_EQ(record->event->struct_size, sizeof(FlutterKeyEvent));
293  EXPECT_EQ(record->event->timestamp, 12345000);
294  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
295  EXPECT_EQ(record->event->physical, kPhysicalDigit1);
296  EXPECT_EQ(record->event->logical, kLogicalDigit1);
297  EXPECT_STREQ(record->event->character, "&");
298  EXPECT_EQ(record->event->synthesized, false);
299 
300  invoke_record_callback(record, TRUE);
301  g_main_loop_run(loop);
302 }
303 
304 // Press Shift, key A, then release Shift, key A.
305 TEST_F(FlKeyEmbedderResponderTest, PressShiftDuringLetterKeyTap) {
306  StartEngine();
307 
308  g_autoptr(FlKeyEmbedderResponder) responder =
310 
311  g_autoptr(GPtrArray) call_records =
312  g_ptr_array_new_with_free_func(g_object_unref);
313  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
314  SendKeyEvent,
315  ([&call_records](auto engine, const FlutterKeyEvent* event,
316  FlutterKeyEventCallback callback, void* user_data) {
317  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
318  event, callback, user_data));
319  return kSuccess;
320  }));
321 
322  // Press shift right
323  g_autoptr(FlKeyEvent) event1 =
324  fl_key_event_new(101, kPress, kKeyCodeShiftRight, GDK_KEY_Shift_R,
325  static_cast<GdkModifierType>(0), 0);
326  g_autoptr(GMainLoop) loop1 = g_main_loop_new(nullptr, 0);
328  responder, event1, 0, nullptr,
329  [](GObject* object, GAsyncResult* result, gpointer user_data) {
330  gboolean handled;
332  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
333  EXPECT_EQ(handled, TRUE);
334 
335  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
336  },
337  loop1);
338 
339  EXPECT_EQ(call_records->len, 1u);
340  FlKeyEmbedderCallRecord* record =
341  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
342  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
343  EXPECT_EQ(record->event->physical, kPhysicalShiftRight);
344  EXPECT_EQ(record->event->logical, kLogicalShiftRight);
345  EXPECT_STREQ(record->event->character, nullptr);
346  EXPECT_EQ(record->event->synthesized, false);
347 
348  invoke_record_callback(record, TRUE);
349  g_main_loop_run(loop1);
350  clear_records(call_records);
351 
352  // Press key A
353  g_autoptr(FlKeyEvent) event2 =
354  fl_key_event_new(102, kPress, kKeyCodeKeyA, GDK_KEY_A, GDK_SHIFT_MASK, 0);
355  g_autoptr(GMainLoop) loop2 = g_main_loop_new(nullptr, 0);
357  responder, event2, 0, nullptr,
358  [](GObject* object, GAsyncResult* result, gpointer user_data) {
359  gboolean handled;
361  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
362  EXPECT_EQ(handled, TRUE);
363 
364  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
365  },
366  loop2);
367 
368  EXPECT_EQ(call_records->len, 1u);
369  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
370  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
371  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
372  EXPECT_EQ(record->event->logical, kLogicalKeyA);
373  EXPECT_STREQ(record->event->character, "A");
374  EXPECT_EQ(record->event->synthesized, false);
375 
376  invoke_record_callback(record, TRUE);
377  g_main_loop_run(loop2);
378  clear_records(call_records);
379 
380  // Release shift right
381  g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
382  103, kRelease, kKeyCodeShiftRight, GDK_KEY_Shift_R, GDK_SHIFT_MASK, 0);
383  g_autoptr(GMainLoop) loop3 = g_main_loop_new(nullptr, 0);
385  responder, event3, 0, nullptr,
386  [](GObject* object, GAsyncResult* result, gpointer user_data) {
387  gboolean handled;
389  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
390  EXPECT_EQ(handled, TRUE);
391 
392  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
393  },
394  loop3);
395 
396  EXPECT_EQ(call_records->len, 1u);
397  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
398  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
399  EXPECT_EQ(record->event->physical, kPhysicalShiftRight);
400  EXPECT_EQ(record->event->logical, kLogicalShiftRight);
401  EXPECT_STREQ(record->event->character, nullptr);
402  EXPECT_EQ(record->event->synthesized, false);
403 
404  invoke_record_callback(record, TRUE);
405  g_main_loop_run(loop3);
406  clear_records(call_records);
407 
408  // Release key A
409  g_autoptr(FlKeyEvent) event4 =
410  fl_key_event_new(104, kRelease, kKeyCodeKeyA, GDK_KEY_A,
411  static_cast<GdkModifierType>(0), 0);
412  g_autoptr(GMainLoop) loop4 = g_main_loop_new(nullptr, 0);
414  responder, event4, 0, nullptr,
415  [](GObject* object, GAsyncResult* result, gpointer user_data) {
416  gboolean handled;
418  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
419  EXPECT_EQ(handled, TRUE);
420 
421  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
422  },
423  loop4);
424 
425  EXPECT_EQ(call_records->len, 1u);
426  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
427  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
428  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
429  EXPECT_EQ(record->event->logical, kLogicalKeyA);
430  EXPECT_STREQ(record->event->character, nullptr);
431  EXPECT_EQ(record->event->synthesized, false);
432 
433  invoke_record_callback(record, TRUE);
434  g_main_loop_run(loop4);
435 }
436 
437 // Press or release Numpad 1 between presses/releases of NumLock.
438 //
439 // This tests interaction between lock keys and non-lock keys in cases that do
440 // not have events missed.
441 //
442 // This also tests the result of Numpad keys across NumLock taps, which is
443 // test-worthy because the keyval for the numpad key will change before and
444 // after the NumLock tap, which should not alter the resulting logical key.
445 TEST_F(FlKeyEmbedderResponderTest, TapNumPadKeysBetweenNumLockEvents) {
446  StartEngine();
447 
448  g_autoptr(FlKeyEmbedderResponder) responder =
450 
451  g_autoptr(GPtrArray) call_records =
452  g_ptr_array_new_with_free_func(g_object_unref);
453  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
454  SendKeyEvent,
455  ([&call_records](auto engine, const FlutterKeyEvent* event,
456  FlutterKeyEventCallback callback, void* user_data) {
457  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
458  event, callback, user_data));
459  return kSuccess;
460  }));
461 
462  // Press Numpad 1 (stage 0)
463  g_autoptr(FlKeyEvent) event1 =
464  fl_key_event_new(101, kPress, kKeyCodeNumpad1, GDK_KEY_KP_End,
465  static_cast<GdkModifierType>(0), 0);
466  g_autoptr(GMainLoop) loop1 = g_main_loop_new(nullptr, 0);
468  responder, event1, 0, nullptr,
469  [](GObject* object, GAsyncResult* result, gpointer user_data) {
470  gboolean handled;
472  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
473  EXPECT_EQ(handled, TRUE);
474 
475  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
476  },
477  loop1);
478 
479  EXPECT_EQ(call_records->len, 1u);
480  FlKeyEmbedderCallRecord* record =
481  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
482  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
483  EXPECT_EQ(record->event->physical, kPhysicalNumpad1);
484  EXPECT_EQ(record->event->logical, kLogicalNumpad1);
485  EXPECT_STREQ(record->event->character, nullptr); // TODO(chrome-bot):
486  EXPECT_EQ(record->event->synthesized, false);
487 
488  invoke_record_callback(record, TRUE);
489  g_main_loop_run(loop1);
490  clear_records(call_records);
491 
492  // Press NumLock (stage 0 -> 1)
493  g_autoptr(FlKeyEvent) event2 =
494  fl_key_event_new(102, kPress, kKeyCodeNumLock, GDK_KEY_Num_Lock,
495  static_cast<GdkModifierType>(0), 0);
496  g_autoptr(GMainLoop) loop2 = g_main_loop_new(nullptr, 0);
498  responder, event2, 0, nullptr,
499  [](GObject* object, GAsyncResult* result, gpointer user_data) {
500  gboolean handled;
502  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
503  EXPECT_EQ(handled, TRUE);
504 
505  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
506  },
507  loop2);
508 
509  EXPECT_EQ(call_records->len, 1u);
510  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
511  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
512  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
513  EXPECT_EQ(record->event->logical, kLogicalNumLock);
514  EXPECT_STREQ(record->event->character, nullptr);
515  EXPECT_EQ(record->event->synthesized, false);
516 
517  invoke_record_callback(record, TRUE);
518  g_main_loop_run(loop2);
519  clear_records(call_records);
520 
521  // Release numpad 1 (stage 1)
522  g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
523  104, kRelease, kKeyCodeNumpad1, GDK_KEY_KP_1, GDK_MOD2_MASK, 0);
524  g_autoptr(GMainLoop) loop3 = g_main_loop_new(nullptr, 0);
526  responder, event3, 0, nullptr,
527  [](GObject* object, GAsyncResult* result, gpointer user_data) {
528  gboolean handled;
530  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
531  EXPECT_EQ(handled, TRUE);
532 
533  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
534  },
535  loop3);
536 
537  EXPECT_EQ(call_records->len, 1u);
538  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
539  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
540  EXPECT_EQ(record->event->physical, kPhysicalNumpad1);
541  EXPECT_EQ(record->event->logical, kLogicalNumpad1);
542  EXPECT_STREQ(record->event->character, nullptr);
543  EXPECT_EQ(record->event->synthesized, false);
544 
545  invoke_record_callback(record, TRUE);
546  g_main_loop_run(loop3);
547  clear_records(call_records);
548 
549  // Release NumLock (stage 1 -> 2)
550  g_autoptr(FlKeyEvent) event4 = fl_key_event_new(
551  103, kRelease, kKeyCodeNumLock, GDK_KEY_Num_Lock, GDK_MOD2_MASK, 0);
552  g_autoptr(GMainLoop) loop4 = g_main_loop_new(nullptr, 0);
554  responder, event4, 0, nullptr,
555  [](GObject* object, GAsyncResult* result, gpointer user_data) {
556  gboolean handled;
558  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
559  EXPECT_EQ(handled, TRUE);
560 
561  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
562  },
563  loop4);
564 
565  EXPECT_EQ(call_records->len, 1u);
566  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
567  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
568  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
569  EXPECT_EQ(record->event->logical, kLogicalNumLock);
570  EXPECT_STREQ(record->event->character, nullptr);
571  EXPECT_EQ(record->event->synthesized, false);
572 
573  invoke_record_callback(record, TRUE);
574  g_main_loop_run(loop4);
575  clear_records(call_records);
576 
577  // Press Numpad 1 (stage 2)
578  g_autoptr(FlKeyEvent) event5 = fl_key_event_new(
579  101, kPress, kKeyCodeNumpad1, GDK_KEY_KP_End, GDK_MOD2_MASK, 0);
580  g_autoptr(GMainLoop) loop5 = g_main_loop_new(nullptr, 0);
582  responder, event5, 0, nullptr,
583  [](GObject* object, GAsyncResult* result, gpointer user_data) {
584  gboolean handled;
586  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
587  EXPECT_EQ(handled, TRUE);
588 
589  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
590  },
591  loop5);
592 
593  EXPECT_EQ(call_records->len, 1u);
594  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
595  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
596  EXPECT_EQ(record->event->physical, kPhysicalNumpad1);
597  EXPECT_EQ(record->event->logical, kLogicalNumpad1);
598  EXPECT_STREQ(record->event->character, nullptr); // TODO(chrome-bot):
599  EXPECT_EQ(record->event->synthesized, false);
600 
601  invoke_record_callback(record, TRUE);
602  g_main_loop_run(loop5);
603  clear_records(call_records);
604 
605  // Press NumLock (stage 2 -> 3)
606  g_autoptr(FlKeyEvent) event6 = fl_key_event_new(
607  102, kPress, kKeyCodeNumLock, GDK_KEY_Num_Lock, GDK_MOD2_MASK, 0);
608  g_autoptr(GMainLoop) loop6 = g_main_loop_new(nullptr, 0);
610  responder, event6, 0, nullptr,
611  [](GObject* object, GAsyncResult* result, gpointer user_data) {
612  gboolean handled;
614  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
615  EXPECT_EQ(handled, TRUE);
616 
617  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
618  },
619  loop6);
620 
621  EXPECT_EQ(call_records->len, 1u);
622  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
623  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
624  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
625  EXPECT_EQ(record->event->logical, kLogicalNumLock);
626  EXPECT_STREQ(record->event->character, nullptr);
627  EXPECT_EQ(record->event->synthesized, false);
628 
629  invoke_record_callback(record, TRUE);
630  g_main_loop_run(loop6);
631  clear_records(call_records);
632 
633  // Release numpad 1 (stage 3)
634  g_autoptr(FlKeyEvent) event7 = fl_key_event_new(
635  104, kRelease, kKeyCodeNumpad1, GDK_KEY_KP_1, GDK_MOD2_MASK, 0);
636  g_autoptr(GMainLoop) loop7 = g_main_loop_new(nullptr, 0);
638  responder, event7, 0, nullptr,
639  [](GObject* object, GAsyncResult* result, gpointer user_data) {
640  gboolean handled;
642  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
643  EXPECT_EQ(handled, TRUE);
644 
645  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
646  },
647  loop7);
648 
649  EXPECT_EQ(call_records->len, 1u);
650  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
651  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
652  EXPECT_EQ(record->event->physical, kPhysicalNumpad1);
653  EXPECT_EQ(record->event->logical, kLogicalNumpad1);
654  EXPECT_STREQ(record->event->character, nullptr);
655  EXPECT_EQ(record->event->synthesized, false);
656 
657  invoke_record_callback(record, TRUE);
658  g_main_loop_run(loop7);
659  clear_records(call_records);
660 
661  // Release NumLock (stage 3 -> 0)
662  g_autoptr(FlKeyEvent) event8 = fl_key_event_new(
663  103, kRelease, kKeyCodeNumLock, GDK_KEY_Num_Lock, GDK_MOD2_MASK, 0);
664  g_autoptr(GMainLoop) loop8 = g_main_loop_new(nullptr, 0);
666  responder, event8, 0, nullptr,
667  [](GObject* object, GAsyncResult* result, gpointer user_data) {
668  gboolean handled;
670  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
671  EXPECT_EQ(handled, TRUE);
672 
673  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
674  },
675  loop8);
676 
677  EXPECT_EQ(call_records->len, 1u);
678  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
679  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
680  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
681  EXPECT_EQ(record->event->logical, kLogicalNumLock);
682  EXPECT_STREQ(record->event->character, nullptr);
683  EXPECT_EQ(record->event->synthesized, false);
684 
685  invoke_record_callback(record, TRUE);
686  g_main_loop_run(loop8);
687 }
688 
689 // Press or release digit 1 between presses/releases of Shift.
690 //
691 // GTK will change the virtual key during a key tap, and the embedder
692 // should regularize it.
693 TEST_F(FlKeyEmbedderResponderTest, ReleaseShiftKeyBetweenDigitKeyEvents) {
694  StartEngine();
695 
696  g_autoptr(FlKeyEmbedderResponder) responder =
698 
699  g_autoptr(GPtrArray) call_records =
700  g_ptr_array_new_with_free_func(g_object_unref);
701  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
702  SendKeyEvent,
703  ([&call_records](auto engine, const FlutterKeyEvent* event,
704  FlutterKeyEventCallback callback, void* user_data) {
705  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
706  event, callback, user_data));
707  return kSuccess;
708  }));
709 
710  GdkModifierType state = static_cast<GdkModifierType>(0);
711 
712  // Press shift left
713  g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
714  101, kPress, kKeyCodeShiftLeft, GDK_KEY_Shift_L, state, 0);
715  g_autoptr(GMainLoop) loop1 = g_main_loop_new(nullptr, 0);
717  responder, event1, 0, nullptr,
718  [](GObject* object, GAsyncResult* result, gpointer user_data) {
719  gboolean handled;
721  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
722  EXPECT_EQ(handled, TRUE);
723 
724  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
725  },
726  loop1);
727 
728  EXPECT_EQ(call_records->len, 1u);
729  FlKeyEmbedderCallRecord* record =
730  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
731  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
732  EXPECT_EQ(record->event->physical, kPhysicalShiftLeft);
733  EXPECT_EQ(record->event->logical, kLogicalShiftLeft);
734  EXPECT_STREQ(record->event->character, nullptr);
735  EXPECT_EQ(record->event->synthesized, false);
736 
737  invoke_record_callback(record, TRUE);
738  g_main_loop_run(loop1);
739  clear_records(call_records);
740 
741  state = GDK_SHIFT_MASK;
742 
743  // Press digit 1, which is '!' on a US keyboard
744  g_autoptr(FlKeyEvent) event2 =
745  fl_key_event_new(102, kPress, kKeyCodeDigit1, GDK_KEY_exclam, state, 0);
746  g_autoptr(GMainLoop) loop2 = g_main_loop_new(nullptr, 0);
748  responder, event2, 0, nullptr,
749  [](GObject* object, GAsyncResult* result, gpointer user_data) {
750  gboolean handled;
752  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
753  EXPECT_EQ(handled, TRUE);
754 
755  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
756  },
757  loop2);
758 
759  EXPECT_EQ(call_records->len, 1u);
760  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
761  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
762  EXPECT_EQ(record->event->physical, kPhysicalDigit1);
763  EXPECT_EQ(record->event->logical, kLogicalExclamation);
764  EXPECT_STREQ(record->event->character, "!");
765  EXPECT_EQ(record->event->synthesized, false);
766 
767  invoke_record_callback(record, TRUE);
768  g_main_loop_run(loop2);
769  clear_records(call_records);
770 
771  // Release shift
772  g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
773  103, kRelease, kKeyCodeShiftLeft, GDK_KEY_Shift_L, state, 0);
774  g_autoptr(GMainLoop) loop3 = g_main_loop_new(nullptr, 0);
776  responder, event3, 0, nullptr,
777  [](GObject* object, GAsyncResult* result, gpointer user_data) {
778  gboolean handled;
780  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
781  EXPECT_EQ(handled, TRUE);
782 
783  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
784  },
785  loop3);
786 
787  EXPECT_EQ(call_records->len, 1u);
788  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
789  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
790  EXPECT_EQ(record->event->physical, kPhysicalShiftLeft);
791  EXPECT_EQ(record->event->logical, kLogicalShiftLeft);
792  EXPECT_STREQ(record->event->character, nullptr);
793  EXPECT_EQ(record->event->synthesized, false);
794 
795  invoke_record_callback(record, TRUE);
796  g_main_loop_run(loop3);
797  clear_records(call_records);
798 
799  state = static_cast<GdkModifierType>(0);
800 
801  // Release digit 1, which is "1" because shift has been released.
802  g_autoptr(FlKeyEvent) event4 =
803  fl_key_event_new(104, kRelease, kKeyCodeDigit1, GDK_KEY_1, state, 0);
804  g_autoptr(GMainLoop) loop4 = g_main_loop_new(nullptr, 0);
806  responder, event4, 0, nullptr,
807  [](GObject* object, GAsyncResult* result, gpointer user_data) {
808  gboolean handled;
810  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
811  EXPECT_EQ(handled, TRUE);
812 
813  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
814  },
815  loop4);
816 
817  EXPECT_EQ(call_records->len, 1u);
818  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
819  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
820  EXPECT_EQ(record->event->physical, kPhysicalDigit1);
821  EXPECT_EQ(record->event->logical, kLogicalExclamation); // Important
822  EXPECT_STREQ(record->event->character, nullptr);
823  EXPECT_EQ(record->event->synthesized, false);
824 
825  invoke_record_callback(record, TRUE);
826  g_main_loop_run(loop4);
827 }
828 
829 // Press or release letter key between presses/releases of CapsLock.
830 //
831 // This tests interaction between lock keys and non-lock keys in cases that do
832 // not have events missed.
833 TEST_F(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEvents) {
834  StartEngine();
835 
836  g_autoptr(FlKeyEmbedderResponder) responder =
838 
839  g_autoptr(GPtrArray) call_records =
840  g_ptr_array_new_with_free_func(g_object_unref);
841  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
842  SendKeyEvent,
843  ([&call_records](auto engine, const FlutterKeyEvent* event,
844  FlutterKeyEventCallback callback, void* user_data) {
845  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
846  event, callback, user_data));
847  return kSuccess;
848  }));
849 
850  // Press CapsLock (stage 0 -> 1)
851  g_autoptr(FlKeyEvent) event1 =
852  fl_key_event_new(101, kPress, kKeyCodeCapsLock, GDK_KEY_Caps_Lock,
853  static_cast<GdkModifierType>(0), 0);
854  g_autoptr(GMainLoop) loop1 = g_main_loop_new(nullptr, 0);
856  responder, event1, 0, nullptr,
857  [](GObject* object, GAsyncResult* result, gpointer user_data) {
858  gboolean handled;
860  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
861  EXPECT_EQ(handled, TRUE);
862 
863  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
864  },
865  loop1);
866 
867  EXPECT_EQ(call_records->len, 1u);
868  FlKeyEmbedderCallRecord* record =
869  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
870  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
871  EXPECT_EQ(record->event->physical, kPhysicalCapsLock);
872  EXPECT_EQ(record->event->logical, kLogicalCapsLock);
873  EXPECT_STREQ(record->event->character, nullptr);
874  EXPECT_EQ(record->event->synthesized, false);
875 
876  invoke_record_callback(record, TRUE);
877  g_main_loop_run(loop1);
878  clear_records(call_records);
879 
880  // Press key A (stage 1)
881  g_autoptr(FlKeyEvent) event2 =
882  fl_key_event_new(102, kPress, kKeyCodeKeyA, GDK_KEY_A, GDK_LOCK_MASK, 0);
883  g_autoptr(GMainLoop) loop2 = g_main_loop_new(nullptr, 0);
885  responder, event2, 0, nullptr,
886  [](GObject* object, GAsyncResult* result, gpointer user_data) {
887  gboolean handled;
889  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
890  EXPECT_EQ(handled, TRUE);
891 
892  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
893  },
894  loop2);
895 
896  EXPECT_EQ(call_records->len, 1u);
897  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
898  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
899  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
900  EXPECT_EQ(record->event->logical, kLogicalKeyA);
901  EXPECT_STREQ(record->event->character, "A");
902  EXPECT_EQ(record->event->synthesized, false);
903 
904  invoke_record_callback(record, TRUE);
905  g_main_loop_run(loop2);
906  clear_records(call_records);
907 
908  // Release CapsLock (stage 1 -> 2)
909  g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
910  103, kRelease, kKeyCodeCapsLock, GDK_KEY_Caps_Lock, GDK_LOCK_MASK, 0);
911  g_autoptr(GMainLoop) loop3 = g_main_loop_new(nullptr, 0);
913  responder, event3, 0, nullptr,
914  [](GObject* object, GAsyncResult* result, gpointer user_data) {
915  gboolean handled;
917  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
918  EXPECT_EQ(handled, TRUE);
919 
920  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
921  },
922  loop3);
923 
924  EXPECT_EQ(call_records->len, 1u);
925  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
926  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
927  EXPECT_EQ(record->event->physical, kPhysicalCapsLock);
928  EXPECT_EQ(record->event->logical, kLogicalCapsLock);
929  EXPECT_STREQ(record->event->character, nullptr);
930  EXPECT_EQ(record->event->synthesized, false);
931 
932  invoke_record_callback(record, TRUE);
933  g_main_loop_run(loop3);
934  clear_records(call_records);
935 
936  // Release key A (stage 2)
937  g_autoptr(FlKeyEvent) event4 = fl_key_event_new(104, kRelease, kKeyCodeKeyA,
938  GDK_KEY_A, GDK_LOCK_MASK, 0);
939  g_autoptr(GMainLoop) loop4 = g_main_loop_new(nullptr, 0);
941  responder, event4, 0, nullptr,
942  [](GObject* object, GAsyncResult* result, gpointer user_data) {
943  gboolean handled;
945  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
946  EXPECT_EQ(handled, TRUE);
947 
948  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
949  },
950  loop4);
951 
952  EXPECT_EQ(call_records->len, 1u);
953  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
954  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
955  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
956  EXPECT_EQ(record->event->logical, kLogicalKeyA);
957  EXPECT_STREQ(record->event->character, nullptr);
958  EXPECT_EQ(record->event->synthesized, false);
959 
960  invoke_record_callback(record, TRUE);
961  g_main_loop_run(loop4);
962  clear_records(call_records);
963 
964  // Press CapsLock (stage 2 -> 3)
965  g_autoptr(FlKeyEvent) event5 = fl_key_event_new(
966  105, kPress, kKeyCodeCapsLock, GDK_KEY_Caps_Lock, GDK_LOCK_MASK, 0);
967  g_autoptr(GMainLoop) loop5 = g_main_loop_new(nullptr, 0);
969  responder, event5, 0, nullptr,
970  [](GObject* object, GAsyncResult* result, gpointer user_data) {
971  gboolean handled;
973  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
974  EXPECT_EQ(handled, TRUE);
975 
976  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
977  },
978  loop5);
979 
980  EXPECT_EQ(call_records->len, 1u);
981  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
982  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
983  EXPECT_EQ(record->event->physical, kPhysicalCapsLock);
984  EXPECT_EQ(record->event->logical, kLogicalCapsLock);
985  EXPECT_STREQ(record->event->character, nullptr);
986  EXPECT_EQ(record->event->synthesized, false);
987 
988  invoke_record_callback(record, TRUE);
989  g_main_loop_run(loop5);
990  clear_records(call_records);
991 
992  // Press key A (stage 3)
993  g_autoptr(FlKeyEvent) event6 =
994  fl_key_event_new(106, kPress, kKeyCodeKeyA, GDK_KEY_A, GDK_LOCK_MASK, 0);
995  g_autoptr(GMainLoop) loop6 = g_main_loop_new(nullptr, 0);
997  responder, event6, 0, nullptr,
998  [](GObject* object, GAsyncResult* result, gpointer user_data) {
999  gboolean handled;
1001  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1002  EXPECT_EQ(handled, TRUE);
1003 
1004  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1005  },
1006  loop6);
1007 
1008  EXPECT_EQ(call_records->len, 1u);
1009  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1010  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1011  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
1012  EXPECT_EQ(record->event->logical, kLogicalKeyA);
1013  EXPECT_STREQ(record->event->character, "A");
1014  EXPECT_EQ(record->event->synthesized, false);
1015 
1016  invoke_record_callback(record, TRUE);
1017  g_main_loop_run(loop6);
1018  clear_records(call_records);
1019 
1020  // Release CapsLock (stage 3 -> 0)
1021  g_autoptr(FlKeyEvent) event7 = fl_key_event_new(
1022  107, kRelease, kKeyCodeCapsLock, GDK_KEY_Caps_Lock, GDK_LOCK_MASK, 0);
1023  g_autoptr(GMainLoop) loop7 = g_main_loop_new(nullptr, 0);
1025  responder, event7, 0, nullptr,
1026  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1027  gboolean handled;
1029  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1030  EXPECT_EQ(handled, TRUE);
1031 
1032  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1033  },
1034  loop7);
1035 
1036  EXPECT_EQ(call_records->len, 1u);
1037  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1038  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
1039  EXPECT_EQ(record->event->physical, kPhysicalCapsLock);
1040  EXPECT_EQ(record->event->logical, kLogicalCapsLock);
1041  EXPECT_STREQ(record->event->character, nullptr);
1042  EXPECT_EQ(record->event->synthesized, false);
1043 
1044  invoke_record_callback(record, TRUE);
1045  g_main_loop_run(loop7);
1046  clear_records(call_records);
1047 
1048  // Release key A (stage 0)
1049  g_autoptr(FlKeyEvent) event8 =
1050  fl_key_event_new(108, kRelease, kKeyCodeKeyA, GDK_KEY_a,
1051  static_cast<GdkModifierType>(0), 0);
1052  g_autoptr(GMainLoop) loop8 = g_main_loop_new(nullptr, 0);
1054  responder, event8, 0, nullptr,
1055  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1056  gboolean handled;
1058  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1059  EXPECT_EQ(handled, TRUE);
1060 
1061  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1062  },
1063  loop8);
1064 
1065  EXPECT_EQ(call_records->len, 1u);
1066  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1067  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
1068  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
1069  EXPECT_EQ(record->event->logical, kLogicalKeyA);
1070  EXPECT_STREQ(record->event->character, nullptr);
1071  EXPECT_EQ(record->event->synthesized, false);
1072 
1073  invoke_record_callback(record, TRUE);
1074  g_main_loop_run(loop8);
1075 }
1076 
1077 // Press or release letter key between presses/releases of CapsLock, on
1078 // a platform with reversed logic.
1079 //
1080 // This happens when using a Chrome remote desktop on MacOS.
1081 TEST_F(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEventsReversed) {
1082  StartEngine();
1083 
1084  g_autoptr(FlKeyEmbedderResponder) responder =
1086 
1087  g_autoptr(GPtrArray) call_records =
1088  g_ptr_array_new_with_free_func(g_object_unref);
1089  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
1090  SendKeyEvent,
1091  ([&call_records](auto engine, const FlutterKeyEvent* event,
1092  FlutterKeyEventCallback callback, void* user_data) {
1093  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
1094  event, callback, user_data));
1095  return kSuccess;
1096  }));
1097 
1098  // Press key A (stage 0)
1099  g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
1100  101, kPress, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
1101  g_autoptr(GMainLoop) loop1 = g_main_loop_new(nullptr, 0);
1103  responder, event1, 0, nullptr,
1104  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1105  gboolean handled;
1107  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1108  EXPECT_EQ(handled, TRUE);
1109 
1110  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1111  },
1112  loop1);
1113 
1114  EXPECT_EQ(call_records->len, 1u);
1115  FlKeyEmbedderCallRecord* record =
1116  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1117  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1118  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
1119  EXPECT_EQ(record->event->logical, kLogicalKeyA);
1120  EXPECT_STREQ(record->event->character, "a");
1121  EXPECT_EQ(record->event->synthesized, false);
1122 
1123  invoke_record_callback(record, TRUE);
1124  g_main_loop_run(loop1);
1125  clear_records(call_records);
1126 
1127  // Press CapsLock (stage 0 -> 1)
1128  g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
1129  102, kPress, kKeyCodeCapsLock, GDK_KEY_Caps_Lock, GDK_LOCK_MASK, 0);
1130  g_autoptr(GMainLoop) loop2 = g_main_loop_new(nullptr, 0);
1132  responder, event2, 0, nullptr,
1133  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1134  gboolean handled;
1136  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1137  EXPECT_EQ(handled, TRUE);
1138 
1139  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1140  },
1141  loop2);
1142 
1143  EXPECT_EQ(call_records->len, 1u);
1144  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1145  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1146  EXPECT_EQ(record->event->physical, kPhysicalCapsLock);
1147  EXPECT_EQ(record->event->logical, kLogicalCapsLock);
1148  EXPECT_STREQ(record->event->character, nullptr);
1149  EXPECT_EQ(record->event->synthesized, false);
1150 
1151  invoke_record_callback(record, TRUE);
1152  g_main_loop_run(loop2);
1153  clear_records(call_records);
1154 
1155  // Release CapsLock (stage 1 -> 2)
1156  g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
1157  103, kRelease, kKeyCodeCapsLock, GDK_KEY_Caps_Lock, GDK_LOCK_MASK, 0);
1158  g_autoptr(GMainLoop) loop3 = g_main_loop_new(nullptr, 0);
1160  responder, event3, 0, nullptr,
1161  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1162  gboolean handled;
1164  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1165  EXPECT_EQ(handled, TRUE);
1166 
1167  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1168  },
1169  loop3);
1170 
1171  EXPECT_EQ(call_records->len, 1u);
1172  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1173  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
1174  EXPECT_EQ(record->event->physical, kPhysicalCapsLock);
1175  EXPECT_EQ(record->event->logical, kLogicalCapsLock);
1176  EXPECT_STREQ(record->event->character, nullptr);
1177  EXPECT_EQ(record->event->synthesized, false);
1178 
1179  invoke_record_callback(record, TRUE);
1180  g_main_loop_run(loop3);
1181  clear_records(call_records);
1182 
1183  // Release key A (stage 2)
1184  g_autoptr(FlKeyEvent) event4 = fl_key_event_new(104, kRelease, kKeyCodeKeyA,
1185  GDK_KEY_A, GDK_LOCK_MASK, 0);
1186  g_autoptr(GMainLoop) loop4 = g_main_loop_new(nullptr, 0);
1188  responder, event4, 0, nullptr,
1189  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1190  gboolean handled;
1192  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1193  EXPECT_EQ(handled, TRUE);
1194 
1195  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1196  },
1197  loop4);
1198 
1199  EXPECT_EQ(call_records->len, 1u);
1200  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1201  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
1202  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
1203  EXPECT_EQ(record->event->logical, kLogicalKeyA);
1204  EXPECT_STREQ(record->event->character, nullptr);
1205  EXPECT_EQ(record->event->synthesized, false);
1206 
1207  invoke_record_callback(record, TRUE);
1208  g_main_loop_run(loop4);
1209  clear_records(call_records);
1210 
1211  // Press key A (stage 2)
1212  g_autoptr(FlKeyEvent) event5 =
1213  fl_key_event_new(105, kPress, kKeyCodeKeyA, GDK_KEY_A, GDK_LOCK_MASK, 0);
1214  g_autoptr(GMainLoop) loop5 = g_main_loop_new(nullptr, 0);
1216  responder, event5, 0, nullptr,
1217  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1218  gboolean handled;
1220  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1221  EXPECT_EQ(handled, TRUE);
1222 
1223  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1224  },
1225  loop5);
1226 
1227  EXPECT_EQ(call_records->len, 1u);
1228  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1229  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1230  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
1231  EXPECT_EQ(record->event->logical, kLogicalKeyA);
1232  EXPECT_STREQ(record->event->character, "A");
1233  EXPECT_EQ(record->event->synthesized, false);
1234 
1235  invoke_record_callback(record, TRUE);
1236  g_main_loop_run(loop5);
1237  clear_records(call_records);
1238 
1239  // Press CapsLock (stage 2 -> 3)
1240  g_autoptr(FlKeyEvent) event6 =
1241  fl_key_event_new(106, kPress, kKeyCodeCapsLock, GDK_KEY_Caps_Lock,
1242  static_cast<GdkModifierType>(0), 0);
1243  g_autoptr(GMainLoop) loop6 = g_main_loop_new(nullptr, 0);
1245  responder, event6, 0, nullptr,
1246  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1247  gboolean handled;
1249  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1250  EXPECT_EQ(handled, TRUE);
1251 
1252  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1253  },
1254  loop6);
1255 
1256  EXPECT_EQ(call_records->len, 1u);
1257  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1258  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1259  EXPECT_EQ(record->event->physical, kPhysicalCapsLock);
1260  EXPECT_EQ(record->event->logical, kLogicalCapsLock);
1261  EXPECT_STREQ(record->event->character, nullptr);
1262  EXPECT_EQ(record->event->synthesized, false);
1263 
1264  invoke_record_callback(record, TRUE);
1265  g_main_loop_run(loop6);
1266  clear_records(call_records);
1267 
1268  // Release CapsLock (stage 3 -> 0)
1269  g_autoptr(FlKeyEvent) event7 = fl_key_event_new(
1270  107, kRelease, kKeyCodeCapsLock, GDK_KEY_Caps_Lock, GDK_LOCK_MASK, 0);
1271  g_autoptr(GMainLoop) loop7 = g_main_loop_new(nullptr, 0);
1273  responder, event7, 0, nullptr,
1274  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1275  gboolean handled;
1277  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1278  EXPECT_EQ(handled, TRUE);
1279 
1280  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1281  },
1282  loop7);
1283 
1284  EXPECT_EQ(call_records->len, 1u);
1285  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1286  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
1287  EXPECT_EQ(record->event->physical, kPhysicalCapsLock);
1288  EXPECT_EQ(record->event->logical, kLogicalCapsLock);
1289  EXPECT_STREQ(record->event->character, nullptr);
1290  EXPECT_EQ(record->event->synthesized, false);
1291 
1292  invoke_record_callback(record, TRUE);
1293  g_main_loop_run(loop7);
1294  clear_records(call_records);
1295 
1296  // Release key A (stage 0)
1297  g_autoptr(FlKeyEvent) event8 =
1298  fl_key_event_new(108, kRelease, kKeyCodeKeyA, GDK_KEY_a,
1299  static_cast<GdkModifierType>(0), 0);
1300  g_autoptr(GMainLoop) loop8 = g_main_loop_new(nullptr, 0);
1302  responder, event8, 0, nullptr,
1303  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1304  gboolean handled;
1306  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1307  EXPECT_EQ(handled, TRUE);
1308 
1309  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1310  },
1311  loop8);
1312 
1313  EXPECT_EQ(call_records->len, 1u);
1314  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1315  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
1316  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
1317  EXPECT_EQ(record->event->logical, kLogicalKeyA);
1318  EXPECT_STREQ(record->event->character, nullptr);
1319  EXPECT_EQ(record->event->synthesized, false);
1320 
1321  invoke_record_callback(record, TRUE);
1322  g_main_loop_run(loop8);
1323 }
1324 
1325 TEST_F(FlKeyEmbedderResponderTest, TurnDuplicateDownEventsToRepeats) {
1326  StartEngine();
1327 
1328  g_autoptr(FlKeyEmbedderResponder) responder =
1330 
1331  g_autoptr(GPtrArray) call_records =
1332  g_ptr_array_new_with_free_func(g_object_unref);
1333  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
1334  SendKeyEvent,
1335  ([&call_records](auto engine, const FlutterKeyEvent* event,
1336  FlutterKeyEventCallback callback, void* user_data) {
1337  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
1338  event, callback, user_data));
1339  return kSuccess;
1340  }));
1341 
1342  // Press KeyA
1343  g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
1344  101, kPress, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
1345  g_autoptr(GMainLoop) loop1 = g_main_loop_new(nullptr, 0);
1347  responder, event1, 0, nullptr,
1348  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1349  gboolean handled;
1351  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1352  EXPECT_EQ(handled, TRUE);
1353 
1354  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1355  },
1356  loop1);
1357 
1358  EXPECT_EQ(call_records->len, 1u);
1359 
1360  FlKeyEmbedderCallRecord* record =
1361  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1362  invoke_record_callback(record, TRUE);
1363  g_main_loop_run(loop1);
1364  clear_records(call_records);
1365 
1366  // Another KeyA down events, which usually means a repeated event.
1367  g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
1368  102, kPress, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
1369  g_autoptr(GMainLoop) loop2 = g_main_loop_new(nullptr, 0);
1371  responder, event2, 0, nullptr,
1372  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1373  gboolean handled;
1375  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1376  EXPECT_EQ(handled, TRUE);
1377 
1378  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1379  },
1380  loop2);
1381 
1382  EXPECT_EQ(call_records->len, 1u);
1383 
1384  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1385  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeRepeat);
1386  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
1387  EXPECT_EQ(record->event->logical, kLogicalKeyA);
1388  EXPECT_STREQ(record->event->character, "a");
1389  EXPECT_EQ(record->event->synthesized, false);
1390  EXPECT_NE(record->callback, nullptr);
1391 
1392  invoke_record_callback(record, TRUE);
1393  g_main_loop_run(loop2);
1394  clear_records(call_records);
1395 
1396  // Release KeyA
1397  g_autoptr(FlKeyEvent) event3 =
1398  fl_key_event_new(103, kRelease, kKeyCodeKeyA, GDK_KEY_q,
1399  static_cast<GdkModifierType>(0), 0);
1400  g_autoptr(GMainLoop) loop3 = g_main_loop_new(nullptr, 0);
1402  responder, event3, 0, nullptr,
1403  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1404  gboolean handled;
1406  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1407  EXPECT_EQ(handled, TRUE);
1408 
1409  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1410  },
1411  loop3);
1412 
1413  EXPECT_EQ(call_records->len, 1u);
1414  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1415  invoke_record_callback(record, TRUE);
1416  g_main_loop_run(loop3);
1417 }
1418 
1419 TEST_F(FlKeyEmbedderResponderTest, IgnoreAbruptUpEvent) {
1420  StartEngine();
1421 
1422  g_autoptr(FlKeyEmbedderResponder) responder =
1424 
1425  g_autoptr(GPtrArray) call_records =
1426  g_ptr_array_new_with_free_func(g_object_unref);
1427  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
1428  SendKeyEvent,
1429  ([&call_records](auto engine, const FlutterKeyEvent* event,
1430  FlutterKeyEventCallback callback, void* user_data) {
1431  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
1432  event, callback, user_data));
1433  return kSuccess;
1434  }));
1435 
1436  // Release KeyA before it was even pressed.
1437  g_autoptr(FlKeyEvent) event =
1438  fl_key_event_new(103, kRelease, kKeyCodeKeyA, GDK_KEY_q,
1439  static_cast<GdkModifierType>(0), 0);
1440  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
1442  responder, event, 0, nullptr,
1443  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1444  gboolean handled;
1446  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1447  EXPECT_EQ(handled, TRUE);
1448 
1449  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1450  },
1451  loop);
1452 
1453  EXPECT_EQ(call_records->len, 1u);
1454 
1455  FlKeyEmbedderCallRecord* record =
1456  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1457  EXPECT_EQ(record->event->physical, 0ull);
1458  EXPECT_EQ(record->event->logical, 0ull);
1459  EXPECT_STREQ(record->event->character, nullptr);
1460  EXPECT_EQ(record->event->synthesized, false);
1461 
1462  invoke_record_callback(record, TRUE);
1463  g_main_loop_run(loop);
1464 }
1465 
1466 // Test if missed modifier keys can be detected and synthesized with state
1467 // information upon events that are for this modifier key.
1469  SynthesizeForDesyncPressingStateOnSelfEvents) {
1470  StartEngine();
1471 
1472  g_autoptr(FlKeyEmbedderResponder) responder =
1474 
1475  g_autoptr(GPtrArray) call_records =
1476  g_ptr_array_new_with_free_func(g_object_unref);
1477  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
1478  SendKeyEvent,
1479  ([&call_records](auto engine, const FlutterKeyEvent* event,
1480  FlutterKeyEventCallback callback, void* user_data) {
1481  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
1482  event, callback, user_data));
1483  return kSuccess;
1484  }));
1485 
1486  // Test 1: synthesize key down.
1487 
1488  // A key down of control left is missed.
1489  GdkModifierType state = GDK_CONTROL_MASK;
1490 
1491  // Send a ControlLeft up
1492  g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
1493  101, kRelease, kKeyCodeControlLeft, GDK_KEY_Control_L, state, 0);
1494  g_autoptr(GMainLoop) loop1 = g_main_loop_new(nullptr, 0);
1496  responder, event1, 0, nullptr,
1497  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1498  gboolean handled;
1500  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1501  EXPECT_EQ(handled, TRUE);
1502 
1503  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1504  },
1505  loop1);
1506 
1507  EXPECT_EQ(call_records->len, 2u);
1508  FlKeyEmbedderCallRecord* record =
1509  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1510  EXPECT_EQ(record->event->timestamp, 101000);
1511  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1512  EXPECT_EQ(record->event->physical, kPhysicalControlLeft);
1513  EXPECT_EQ(record->event->logical, kLogicalControlLeft);
1514  EXPECT_STREQ(record->event->character, nullptr);
1515  EXPECT_EQ(record->event->synthesized, true);
1516 
1517  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 1));
1518  EXPECT_EQ(record->event->timestamp, 101000);
1519  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
1520  EXPECT_EQ(record->event->physical, kPhysicalControlLeft);
1521  EXPECT_EQ(record->event->logical, kLogicalControlLeft);
1522  EXPECT_STREQ(record->event->character, nullptr);
1523  EXPECT_EQ(record->event->synthesized, false);
1524 
1525  invoke_record_callback(record, TRUE);
1526  g_main_loop_run(loop1);
1527  clear_records(call_records);
1528 
1529  // Test 2: synthesize key up.
1530 
1531  // Send a ControlLeft down.
1532  state = static_cast<GdkModifierType>(0);
1533  g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
1534  102, kPress, kKeyCodeControlLeft, GDK_KEY_Control_L, state, 0);
1535  g_autoptr(GMainLoop) loop2 = g_main_loop_new(nullptr, 0);
1537  responder, event2, 0, nullptr,
1538  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1539  gboolean handled;
1541  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1542  EXPECT_EQ(handled, TRUE);
1543 
1544  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1545  },
1546  loop2);
1547  EXPECT_EQ(call_records->len, 1u);
1548  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1549  invoke_record_callback(record, TRUE);
1550  g_main_loop_run(loop2);
1551  clear_records(call_records);
1552 
1553  // A key up of control left is missed.
1554  state = static_cast<GdkModifierType>(0);
1555 
1556  // Send another ControlLeft down
1557  g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
1558  103, kPress, kKeyCodeControlLeft, GDK_KEY_Control_L, state, 0);
1559  g_autoptr(GMainLoop) loop3 = g_main_loop_new(nullptr, 0);
1561  responder, event3, 0, nullptr,
1562  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1563  gboolean handled;
1565  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1566  EXPECT_EQ(handled, TRUE);
1567 
1568  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1569  },
1570  loop3);
1571 
1572  EXPECT_EQ(call_records->len, 2u);
1573  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1574  EXPECT_EQ(record->event->timestamp, 103000);
1575  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
1576  EXPECT_EQ(record->event->physical, kPhysicalControlLeft);
1577  EXPECT_EQ(record->event->logical, kLogicalControlLeft);
1578  EXPECT_STREQ(record->event->character, nullptr);
1579  EXPECT_EQ(record->event->synthesized, true);
1580 
1581  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 1));
1582  EXPECT_EQ(record->event->timestamp, 103000);
1583  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1584  EXPECT_EQ(record->event->physical, kPhysicalControlLeft);
1585  EXPECT_EQ(record->event->logical, kLogicalControlLeft);
1586  EXPECT_STREQ(record->event->character, nullptr);
1587  EXPECT_EQ(record->event->synthesized, false);
1588 
1589  invoke_record_callback(record, TRUE);
1590  g_main_loop_run(loop3);
1591  clear_records(call_records);
1592 
1593  // Send a ControlLeft up to clear up state.
1594  state = GDK_CONTROL_MASK;
1595  g_autoptr(FlKeyEvent) event4 = fl_key_event_new(
1596  104, kRelease, kKeyCodeControlLeft, GDK_KEY_Control_L, state, 0);
1597  g_autoptr(GMainLoop) loop4 = g_main_loop_new(nullptr, 0);
1599  responder, event4, 0, nullptr,
1600  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1601  gboolean handled;
1603  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1604  EXPECT_EQ(handled, TRUE);
1605 
1606  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1607  },
1608  loop4);
1609  EXPECT_EQ(call_records->len, 1u);
1610  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1611  invoke_record_callback(record, TRUE);
1612  g_main_loop_run(loop4);
1613  clear_records(call_records);
1614 
1615  // Test 3: synthesize by right modifier.
1616 
1617  // A key down of control right is missed.
1618  state = GDK_CONTROL_MASK;
1619 
1620  // Send a ControlRight up.
1621  g_autoptr(FlKeyEvent) event5 = fl_key_event_new(
1622  105, kRelease, kKeyCodeControlRight, GDK_KEY_Control_R, state, 0);
1623  g_autoptr(GMainLoop) loop5 = g_main_loop_new(nullptr, 0);
1625  responder, event5, 0, nullptr,
1626  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1627  gboolean handled;
1629  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1630  EXPECT_EQ(handled, TRUE);
1631 
1632  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1633  },
1634  loop5);
1635 
1636  // A ControlLeft down is synthesized, with an empty event.
1637  // Reason: The ControlLeft down is synthesized to synchronize the state
1638  // showing Control as pressed. The ControlRight event is ignored because
1639  // the event is considered a duplicate up event.
1640  EXPECT_EQ(call_records->len, 1u);
1641  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1642  EXPECT_EQ(record->event->timestamp, 105000);
1643  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1644  EXPECT_EQ(record->event->physical, kPhysicalControlLeft);
1645  EXPECT_EQ(record->event->logical, kLogicalControlLeft);
1646  EXPECT_STREQ(record->event->character, nullptr);
1647  EXPECT_EQ(record->event->synthesized, true);
1648 
1649  invoke_record_callback(record, TRUE);
1650  g_main_loop_run(loop5);
1651 }
1652 
1653 // Test if missed modifier keys can be detected and synthesized with state
1654 // information upon events that are not for this modifier key.
1656  SynthesizeForDesyncPressingStateOnNonSelfEvents) {
1657  StartEngine();
1658 
1659  g_autoptr(FlKeyEmbedderResponder) responder =
1661 
1662  g_autoptr(GPtrArray) call_records =
1663  g_ptr_array_new_with_free_func(g_object_unref);
1664  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
1665  SendKeyEvent,
1666  ([&call_records](auto engine, const FlutterKeyEvent* event,
1667  FlutterKeyEventCallback callback, void* user_data) {
1668  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
1669  event, callback, user_data));
1670  return kSuccess;
1671  }));
1672 
1673  // A key down of control left is missed.
1674  GdkModifierType state = GDK_CONTROL_MASK;
1675 
1676  // Send a normal event (KeyA down)
1677  g_autoptr(FlKeyEvent) event1 =
1678  fl_key_event_new(101, kPress, kKeyCodeKeyA, GDK_KEY_a, state, 0);
1679  g_autoptr(GMainLoop) loop1 = g_main_loop_new(nullptr, 0);
1681  responder, event1, 0, nullptr,
1682  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1683  gboolean handled;
1685  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1686  EXPECT_EQ(handled, TRUE);
1687 
1688  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1689  },
1690  loop1);
1691 
1692  EXPECT_EQ(call_records->len, 2u);
1693  FlKeyEmbedderCallRecord* record =
1694  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1695  EXPECT_EQ(record->event->timestamp, 101000);
1696  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1697  EXPECT_EQ(record->event->physical, kPhysicalControlLeft);
1698  EXPECT_EQ(record->event->logical, kLogicalControlLeft);
1699  EXPECT_STREQ(record->event->character, nullptr);
1700  EXPECT_EQ(record->event->synthesized, true);
1701 
1702  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 1));
1703  EXPECT_EQ(record->event->timestamp, 101000);
1704  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1705  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
1706  EXPECT_EQ(record->event->logical, kLogicalKeyA);
1707  EXPECT_STREQ(record->event->character, "a");
1708  EXPECT_EQ(record->event->synthesized, false);
1709 
1710  invoke_record_callback(record, TRUE);
1711  g_main_loop_run(loop1);
1712  clear_records(call_records);
1713 
1714  // A key up of control left is missed.
1715  state = static_cast<GdkModifierType>(0);
1716 
1717  // Send a normal event (KeyA up)
1718  g_autoptr(FlKeyEvent) event2 =
1719  fl_key_event_new(102, kRelease, kKeyCodeKeyA, GDK_KEY_A, state, 0);
1720  g_autoptr(GMainLoop) loop2 = g_main_loop_new(nullptr, 0);
1722  responder, event2, 0, nullptr,
1723  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1724  gboolean handled;
1726  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1727  EXPECT_EQ(handled, TRUE);
1728 
1729  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1730  },
1731  loop2);
1732 
1733  EXPECT_EQ(call_records->len, 2u);
1734  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1735  EXPECT_EQ(record->event->timestamp, 102000);
1736  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
1737  EXPECT_EQ(record->event->physical, kPhysicalControlLeft);
1738  EXPECT_EQ(record->event->logical, kLogicalControlLeft);
1739  EXPECT_STREQ(record->event->character, nullptr);
1740  EXPECT_EQ(record->event->synthesized, true);
1741 
1742  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 1));
1743  EXPECT_EQ(record->event->timestamp, 102000);
1744  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
1745  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
1746  EXPECT_EQ(record->event->logical, kLogicalKeyA);
1747  EXPECT_STREQ(record->event->character, nullptr);
1748  EXPECT_EQ(record->event->synthesized, false);
1749 
1750  invoke_record_callback(record, TRUE);
1751  g_main_loop_run(loop2);
1752  clear_records(call_records);
1753 
1754  // Test non-default key mapping.
1755 
1756  // Press a key with physical CapsLock and logical ControlLeft.
1757  state = static_cast<GdkModifierType>(0);
1758 
1759  g_autoptr(FlKeyEvent) event3 = fl_key_event_new(101, kPress, kKeyCodeCapsLock,
1760  GDK_KEY_Control_L, state, 0);
1761  g_autoptr(GMainLoop) loop3 = g_main_loop_new(nullptr, 0);
1763  responder, event3, 0, nullptr,
1764  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1765  gboolean handled;
1767  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1768  EXPECT_EQ(handled, TRUE);
1769 
1770  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1771  },
1772  loop3);
1773 
1774  EXPECT_EQ(call_records->len, 1u);
1775  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1776  EXPECT_EQ(record->event->timestamp, 101000);
1777  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1778  EXPECT_EQ(record->event->physical, kPhysicalCapsLock);
1779  EXPECT_EQ(record->event->logical, kLogicalControlLeft);
1780  EXPECT_STREQ(record->event->character, nullptr);
1781  EXPECT_EQ(record->event->synthesized, false);
1782 
1783  invoke_record_callback(record, TRUE);
1784  g_main_loop_run(loop3);
1785  clear_records(call_records);
1786 
1787  // The key up of the control left press is missed.
1788  state = static_cast<GdkModifierType>(0);
1789 
1790  // Send a normal event (KeyA down).
1791  g_autoptr(FlKeyEvent) event4 =
1792  fl_key_event_new(102, kPress, kKeyCodeKeyA, GDK_KEY_A, state, 0);
1793  g_autoptr(GMainLoop) loop4 = g_main_loop_new(nullptr, 0);
1795  responder, event4, 0, nullptr,
1796  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1797  gboolean handled;
1799  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1800  EXPECT_EQ(handled, TRUE);
1801 
1802  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1803  },
1804  loop4);
1805 
1806  // The synthesized event should have physical CapsLock and logical
1807  // ControlLeft.
1808  EXPECT_EQ(call_records->len, 2u);
1809  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1810  EXPECT_EQ(record->event->timestamp, 102000);
1811  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
1812  EXPECT_EQ(record->event->physical, kPhysicalCapsLock);
1813  EXPECT_EQ(record->event->logical, kLogicalControlLeft);
1814  EXPECT_STREQ(record->event->character, nullptr);
1815  EXPECT_EQ(record->event->synthesized, true);
1816 
1817  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 1));
1818  EXPECT_EQ(record->event->timestamp, 102000);
1819  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1820  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
1821  EXPECT_EQ(record->event->logical, kLogicalKeyA);
1822  EXPECT_STREQ(record->event->character, "A");
1823  EXPECT_EQ(record->event->synthesized, false);
1824 
1825  invoke_record_callback(record, TRUE);
1826  g_main_loop_run(loop4);
1827 }
1828 
1829 // Test if missed modifier keys can be detected and synthesized with state
1830 // information upon events that do not have the standard key mapping.
1832  SynthesizeForDesyncPressingStateOnRemappedEvents) {
1833  StartEngine();
1834 
1835  g_autoptr(FlKeyEmbedderResponder) responder =
1837 
1838  g_autoptr(GPtrArray) call_records =
1839  g_ptr_array_new_with_free_func(g_object_unref);
1840  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
1841  SendKeyEvent,
1842  ([&call_records](auto engine, const FlutterKeyEvent* event,
1843  FlutterKeyEventCallback callback, void* user_data) {
1844  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
1845  event, callback, user_data));
1846  return kSuccess;
1847  }));
1848 
1849  // Press a key with physical CapsLock and logical ControlLeft.
1850  GdkModifierType state = static_cast<GdkModifierType>(0);
1851 
1852  g_autoptr(FlKeyEvent) event1 = fl_key_event_new(101, kPress, kKeyCodeCapsLock,
1853  GDK_KEY_Control_L, state, 0);
1854  g_autoptr(GMainLoop) loop1 = g_main_loop_new(nullptr, 0);
1856  responder, event1, 0, nullptr,
1857  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1858  gboolean handled;
1860  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1861  EXPECT_EQ(handled, TRUE);
1862 
1863  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1864  },
1865  loop1);
1866 
1867  EXPECT_EQ(call_records->len, 1u);
1868  FlKeyEmbedderCallRecord* record =
1869  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1870  EXPECT_EQ(record->event->timestamp, 101000);
1871  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1872  EXPECT_EQ(record->event->physical, kPhysicalCapsLock);
1873  EXPECT_EQ(record->event->logical, kLogicalControlLeft);
1874  EXPECT_STREQ(record->event->character, nullptr);
1875  EXPECT_EQ(record->event->synthesized, false);
1876 
1877  invoke_record_callback(record, TRUE);
1878  g_main_loop_run(loop1);
1879  clear_records(call_records);
1880 
1881  // The key up of the control left press is missed.
1882  state = static_cast<GdkModifierType>(0);
1883 
1884  // Send a normal event (KeyA down).
1885  g_autoptr(FlKeyEvent) event2 =
1886  fl_key_event_new(102, kPress, kKeyCodeKeyA, GDK_KEY_A, state, 0);
1887  g_autoptr(GMainLoop) loop2 = g_main_loop_new(nullptr, 0);
1889  responder, event2, 0, nullptr,
1890  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1891  gboolean handled;
1893  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1894  EXPECT_EQ(handled, TRUE);
1895 
1896  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1897  },
1898  loop2);
1899 
1900  // The synthesized event should have physical CapsLock and logical
1901  // ControlLeft.
1902  EXPECT_EQ(call_records->len, 2u);
1903  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1904  EXPECT_EQ(record->event->timestamp, 102000);
1905  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
1906  EXPECT_EQ(record->event->physical, kPhysicalCapsLock);
1907  EXPECT_EQ(record->event->logical, kLogicalControlLeft);
1908  EXPECT_STREQ(record->event->character, nullptr);
1909  EXPECT_EQ(record->event->synthesized, true);
1910 
1911  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 1));
1912  EXPECT_EQ(record->event->timestamp, 102000);
1913  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1914  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
1915  EXPECT_EQ(record->event->logical, kLogicalKeyA);
1916  EXPECT_STREQ(record->event->character, "A");
1917  EXPECT_EQ(record->event->synthesized, false);
1918 
1919  invoke_record_callback(record, TRUE);
1920  g_main_loop_run(loop2);
1921 }
1922 
1923 // Test if missed lock keys can be detected and synthesized with state
1924 // information upon events that are not for this modifier key.
1925 TEST_F(FlKeyEmbedderResponderTest, SynthesizeForDesyncLockModeOnNonSelfEvents) {
1926  StartEngine();
1927 
1928  g_autoptr(FlKeyEmbedderResponder) responder =
1930 
1931  g_autoptr(GPtrArray) call_records =
1932  g_ptr_array_new_with_free_func(g_object_unref);
1933  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
1934  SendKeyEvent,
1935  ([&call_records](auto engine, const FlutterKeyEvent* event,
1936  FlutterKeyEventCallback callback, void* user_data) {
1937  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
1938  event, callback, user_data));
1939  return kSuccess;
1940  }));
1941 
1942  // The NumLock is desynchronized by being enabled.
1943  GdkModifierType state = GDK_MOD2_MASK;
1944 
1945  // Send a normal event
1946  g_autoptr(FlKeyEvent) event1 =
1947  fl_key_event_new(101, kPress, kKeyCodeKeyA, GDK_KEY_a, state, 0);
1948  g_autoptr(GMainLoop) loop1 = g_main_loop_new(nullptr, 0);
1950  responder, event1, 0, nullptr,
1951  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1952  gboolean handled;
1954  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1955  EXPECT_EQ(handled, TRUE);
1956 
1957  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1958  },
1959  loop1);
1960 
1961  EXPECT_EQ(call_records->len, 2u);
1962  FlKeyEmbedderCallRecord* record =
1963  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
1964  EXPECT_EQ(record->event->timestamp, 101000);
1965  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1966  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
1967  EXPECT_EQ(record->event->logical, kLogicalNumLock);
1968  EXPECT_STREQ(record->event->character, nullptr);
1969  EXPECT_EQ(record->event->synthesized, true);
1970 
1971  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 1));
1972  EXPECT_EQ(record->event->timestamp, 101000);
1973  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
1974  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
1975  EXPECT_EQ(record->event->logical, kLogicalKeyA);
1976  EXPECT_STREQ(record->event->character, "a");
1977  EXPECT_EQ(record->event->synthesized, false);
1978 
1979  invoke_record_callback(record, TRUE);
1980  g_main_loop_run(loop1);
1981  clear_records(call_records);
1982 
1983  // The NumLock is desynchronized by being disabled.
1984  state = static_cast<GdkModifierType>(0);
1985 
1986  // Release key A
1987  g_autoptr(FlKeyEvent) event2 =
1988  fl_key_event_new(102, kRelease, kKeyCodeKeyA, GDK_KEY_A, state, 0);
1989  g_autoptr(GMainLoop) loop2 = g_main_loop_new(nullptr, 0);
1991  responder, event2, 0, nullptr,
1992  [](GObject* object, GAsyncResult* result, gpointer user_data) {
1993  gboolean handled;
1995  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
1996  EXPECT_EQ(handled, TRUE);
1997 
1998  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
1999  },
2000  loop2);
2001 
2002  EXPECT_EQ(call_records->len, 4u);
2003  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
2004  EXPECT_EQ(record->event->timestamp, 102000);
2005  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
2006  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
2007  EXPECT_EQ(record->event->logical, kLogicalNumLock);
2008  EXPECT_STREQ(record->event->character, nullptr);
2009  EXPECT_EQ(record->event->synthesized, true);
2010 
2011  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 1));
2012  EXPECT_EQ(record->event->timestamp, 102000);
2013  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2014  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
2015  EXPECT_EQ(record->event->logical, kLogicalNumLock);
2016  EXPECT_STREQ(record->event->character, nullptr);
2017  EXPECT_EQ(record->event->synthesized, true);
2018 
2019  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 2));
2020  EXPECT_EQ(record->event->timestamp, 102000);
2021  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
2022  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
2023  EXPECT_EQ(record->event->logical, kLogicalNumLock);
2024  EXPECT_STREQ(record->event->character, nullptr);
2025  EXPECT_EQ(record->event->synthesized, true);
2026 
2027  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 3));
2028  EXPECT_EQ(record->event->timestamp, 102000);
2029  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
2030  EXPECT_EQ(record->event->physical, kPhysicalKeyA);
2031  EXPECT_EQ(record->event->logical, kLogicalKeyA);
2032  EXPECT_STREQ(record->event->character, nullptr);
2033  EXPECT_EQ(record->event->synthesized, false);
2034 
2035  invoke_record_callback(record, TRUE);
2036  g_main_loop_run(loop2);
2037  clear_records(call_records);
2038 
2039  // Release NumLock. Since the previous event should have synthesized NumLock
2040  // to be released, this should result in only an empty event.
2041  g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
2042  103, kRelease, kKeyCodeNumLock, GDK_KEY_Num_Lock, state, 0);
2043  g_autoptr(GMainLoop) loop3 = g_main_loop_new(nullptr, 0);
2045  responder, event3, 0, nullptr,
2046  [](GObject* object, GAsyncResult* result, gpointer user_data) {
2047  gboolean handled;
2049  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
2050  EXPECT_EQ(handled, TRUE);
2051 
2052  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
2053  },
2054  loop3);
2055 
2056  EXPECT_EQ(call_records->len, 1u);
2057  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
2058  EXPECT_EQ(record->event->physical, 0ull);
2059  EXPECT_EQ(record->event->logical, 0ull);
2060  EXPECT_STREQ(record->event->character, nullptr);
2061  EXPECT_EQ(record->event->synthesized, false);
2062 
2063  invoke_record_callback(record, TRUE);
2064  g_main_loop_run(loop3);
2065 }
2066 
2067 // Test if missed lock keys can be detected and synthesized with state
2068 // information upon events that are for this modifier key.
2069 TEST_F(FlKeyEmbedderResponderTest, SynthesizeForDesyncLockModeOnSelfEvents) {
2070  StartEngine();
2071 
2072  g_autoptr(FlKeyEmbedderResponder) responder =
2074 
2075  g_autoptr(GPtrArray) call_records =
2076  g_ptr_array_new_with_free_func(g_object_unref);
2077  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
2078  SendKeyEvent,
2079  ([&call_records](auto engine, const FlutterKeyEvent* event,
2080  FlutterKeyEventCallback callback, void* user_data) {
2081  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
2082  event, callback, user_data));
2083  return kSuccess;
2084  }));
2085 
2086  // The NumLock is desynchronized by being enabled.
2087  GdkModifierType state = GDK_MOD2_MASK;
2088 
2089  // NumLock down
2090  g_autoptr(FlKeyEvent) event1 = fl_key_event_new(101, kPress, kKeyCodeNumLock,
2091  GDK_KEY_Num_Lock, state, 0);
2092  g_autoptr(GMainLoop) loop1 = g_main_loop_new(nullptr, 0);
2094  responder, event1, 0, nullptr,
2095  [](GObject* object, GAsyncResult* result, gpointer user_data) {
2096  gboolean handled;
2098  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
2099  EXPECT_EQ(handled, TRUE);
2100 
2101  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
2102  },
2103  loop1);
2104 
2105  EXPECT_EQ(call_records->len, 3u);
2106  FlKeyEmbedderCallRecord* record =
2107  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
2108  EXPECT_EQ(record->event->timestamp, 101000);
2109  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2110  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
2111  EXPECT_EQ(record->event->logical, kLogicalNumLock);
2112  EXPECT_STREQ(record->event->character, nullptr);
2113  EXPECT_EQ(record->event->synthesized, true);
2114 
2115  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 1));
2116  EXPECT_EQ(record->event->timestamp, 101000);
2117  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
2118  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
2119  EXPECT_EQ(record->event->logical, kLogicalNumLock);
2120  EXPECT_STREQ(record->event->character, nullptr);
2121  EXPECT_EQ(record->event->synthesized, true);
2122 
2123  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 2));
2124  EXPECT_EQ(record->event->timestamp, 101000);
2125  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2126  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
2127  EXPECT_EQ(record->event->logical, kLogicalNumLock);
2128  EXPECT_STREQ(record->event->character, nullptr);
2129  EXPECT_EQ(record->event->synthesized, false);
2130 
2131  invoke_record_callback(record, TRUE);
2132  g_main_loop_run(loop1);
2133  clear_records(call_records);
2134 
2135  // The NumLock is desynchronized by being enabled in a press event.
2136  state = GDK_MOD2_MASK;
2137 
2138  // NumLock up
2139  g_autoptr(FlKeyEvent) event2 = fl_key_event_new(102, kPress, kKeyCodeNumLock,
2140  GDK_KEY_Num_Lock, state, 0);
2141  g_autoptr(GMainLoop) loop2 = g_main_loop_new(nullptr, 0);
2143  responder, event2, 0, nullptr,
2144  [](GObject* object, GAsyncResult* result, gpointer user_data) {
2145  gboolean handled;
2147  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
2148  EXPECT_EQ(handled, TRUE);
2149 
2150  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
2151  },
2152  loop2);
2153 
2154  EXPECT_EQ(call_records->len, 4u);
2155  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
2156  EXPECT_EQ(record->event->timestamp, 102000);
2157  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
2158  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
2159  EXPECT_EQ(record->event->logical, kLogicalNumLock);
2160  EXPECT_STREQ(record->event->character, nullptr);
2161  EXPECT_EQ(record->event->synthesized, true);
2162 
2163  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 1));
2164  EXPECT_EQ(record->event->timestamp, 102000);
2165  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2166  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
2167  EXPECT_EQ(record->event->logical, kLogicalNumLock);
2168  EXPECT_STREQ(record->event->character, nullptr);
2169  EXPECT_EQ(record->event->synthesized, true);
2170 
2171  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 2));
2172  EXPECT_EQ(record->event->timestamp, 102000);
2173  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
2174  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
2175  EXPECT_EQ(record->event->logical, kLogicalNumLock);
2176  EXPECT_STREQ(record->event->character, nullptr);
2177  EXPECT_EQ(record->event->synthesized, true);
2178 
2179  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 3));
2180  EXPECT_EQ(record->event->timestamp, 102000);
2181  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2182  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
2183  EXPECT_EQ(record->event->logical, kLogicalNumLock);
2184  EXPECT_STREQ(record->event->character, nullptr);
2185  EXPECT_EQ(record->event->synthesized, false);
2186 
2187  invoke_record_callback(record, TRUE);
2188  g_main_loop_run(loop2);
2189 }
2190 
2191 // Ensures that even if the primary event is ignored (due to duplicate
2192 // key up or down events), key synthesization is still performed.
2193 TEST_F(FlKeyEmbedderResponderTest, SynthesizationOccursOnIgnoredEvents) {
2194  StartEngine();
2195 
2196  g_autoptr(FlKeyEmbedderResponder) responder =
2198 
2199  g_autoptr(GPtrArray) call_records =
2200  g_ptr_array_new_with_free_func(g_object_unref);
2201  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
2202  SendKeyEvent,
2203  ([&call_records](auto engine, const FlutterKeyEvent* event,
2204  FlutterKeyEventCallback callback, void* user_data) {
2205  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
2206  event, callback, user_data));
2207  return kSuccess;
2208  }));
2209 
2210  // The NumLock is desynchronized by being enabled, and Control is pressed.
2211  GdkModifierType state =
2212  static_cast<GdkModifierType>(GDK_MOD2_MASK | GDK_CONTROL_MASK);
2213 
2214  // Send a KeyA up event, which will be ignored.
2215  g_autoptr(FlKeyEvent) event =
2216  fl_key_event_new(101, kRelease, kKeyCodeKeyA, GDK_KEY_a, state, 0);
2217  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
2219  responder, event, 0, nullptr,
2220  [](GObject* object, GAsyncResult* result, gpointer user_data) {
2221  gboolean handled;
2223  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
2224  EXPECT_EQ(handled, TRUE);
2225 
2226  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
2227  },
2228  loop);
2229  g_main_loop_run(loop);
2230 
2231  EXPECT_EQ(call_records->len, 2u);
2232  FlKeyEmbedderCallRecord* record =
2233  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
2234  EXPECT_EQ(record->event->timestamp, 101000);
2235  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2236  EXPECT_EQ(record->event->physical, kPhysicalNumLock);
2237  EXPECT_EQ(record->event->logical, kLogicalNumLock);
2238  EXPECT_STREQ(record->event->character, nullptr);
2239  EXPECT_EQ(record->event->synthesized, true);
2240 
2241  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 1));
2242  EXPECT_EQ(record->event->timestamp, 101000);
2243  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2244  EXPECT_EQ(record->event->physical, kPhysicalControlLeft);
2245  EXPECT_EQ(record->event->logical, kLogicalControlLeft);
2246  EXPECT_STREQ(record->event->character, nullptr);
2247  EXPECT_EQ(record->event->synthesized, true);
2248 }
2249 
2250 // This test case occurs when the following two cases collide:
2251 //
2252 // 1. When holding shift, AltRight key gives logical GDK_KEY_Meta_R with the
2253 // state bitmask still MOD3 (Alt).
2254 // 2. When holding AltRight, ShiftLeft key gives logical GDK_KEY_ISO_Next_Group
2255 // with the state bitmask RESERVED_14.
2256 //
2257 // The resulting event sequence is not perfectly ideal: it had to synthesize
2258 // AltLeft down because the physical AltRight key corresponds to logical
2259 // MetaRight at the moment.
2260 TEST_F(FlKeyEmbedderResponderTest, HandlesShiftAltVersusGroupNext) {
2261  StartEngine();
2262 
2263  g_autoptr(FlKeyEmbedderResponder) responder =
2265 
2266  g_autoptr(GPtrArray) call_records =
2267  g_ptr_array_new_with_free_func(g_object_unref);
2268  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
2269  SendKeyEvent,
2270  ([&call_records](auto engine, const FlutterKeyEvent* event,
2271  FlutterKeyEventCallback callback, void* user_data) {
2272  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
2273  event, callback, user_data));
2274  callback(true, user_data);
2275  return kSuccess;
2276  }));
2277 
2278  guint32 now_time = 1;
2279  // A convenient shorthand to simulate events.
2280  auto send_key_event = [responder, &now_time](bool is_press, guint keyval,
2281  guint16 keycode,
2282  GdkModifierType state) {
2283  now_time += 1;
2284  g_autoptr(FlKeyEvent) event =
2285  fl_key_event_new(now_time, is_press, keycode, keyval, state, 0);
2286  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
2288  responder, event, 0, nullptr,
2289  [](GObject* object, GAsyncResult* result, gpointer user_data) {
2290  gboolean handled;
2292  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
2293  EXPECT_EQ(handled, TRUE);
2294 
2295  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
2296  },
2297  loop);
2298  g_main_loop_run(loop);
2299  };
2300 
2301  send_key_event(kPress, GDK_KEY_Shift_L, kKeyCodeShiftLeft,
2302  GDK_MODIFIER_RESERVED_25_MASK);
2303  EXPECT_EQ(call_records->len, 1u);
2304  FlKeyEmbedderCallRecord* record =
2305  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
2306  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2307  EXPECT_EQ(record->event->physical, kPhysicalShiftLeft);
2308  EXPECT_EQ(record->event->logical, kLogicalShiftLeft);
2309  EXPECT_EQ(record->event->synthesized, false);
2310 
2311  send_key_event(kPress, GDK_KEY_Meta_R, kKeyCodeAltRight,
2312  static_cast<GdkModifierType>(GDK_SHIFT_MASK |
2313  GDK_MODIFIER_RESERVED_25_MASK));
2314  EXPECT_EQ(call_records->len, 2u);
2315  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 1));
2316  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2317  EXPECT_EQ(record->event->physical, kPhysicalAltRight);
2318  EXPECT_EQ(record->event->logical, kLogicalMetaRight);
2319  EXPECT_EQ(record->event->synthesized, false);
2320 
2321  send_key_event(kRelease, GDK_KEY_ISO_Next_Group, kKeyCodeShiftLeft,
2322  static_cast<GdkModifierType>(GDK_SHIFT_MASK | GDK_MOD1_MASK |
2323  GDK_MODIFIER_RESERVED_25_MASK));
2324  EXPECT_EQ(call_records->len, 5u);
2325  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 2));
2326  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2327  EXPECT_EQ(record->event->physical, kPhysicalAltLeft);
2328  EXPECT_EQ(record->event->logical, kLogicalAltLeft);
2329  EXPECT_EQ(record->event->synthesized, true);
2330 
2331  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 3));
2332  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
2333  EXPECT_EQ(record->event->physical, kPhysicalAltRight);
2334  EXPECT_EQ(record->event->logical, kLogicalMetaRight);
2335  EXPECT_EQ(record->event->synthesized, true);
2336 
2337  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 4));
2338  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
2339  EXPECT_EQ(record->event->physical, kPhysicalShiftLeft);
2340  EXPECT_EQ(record->event->logical, kLogicalShiftLeft);
2341  EXPECT_EQ(record->event->synthesized, false);
2342 
2343  send_key_event(kPress, GDK_KEY_ISO_Next_Group, kKeyCodeShiftLeft,
2344  static_cast<GdkModifierType>(GDK_MOD1_MASK |
2345  GDK_MODIFIER_RESERVED_25_MASK));
2346  EXPECT_EQ(call_records->len, 6u);
2347  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 5));
2348  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2349  EXPECT_EQ(record->event->physical, kPhysicalShiftLeft);
2350  EXPECT_EQ(record->event->logical, kLogicalGroupNext);
2351  EXPECT_EQ(record->event->synthesized, false);
2352 
2353  send_key_event(kRelease, GDK_KEY_ISO_Level3_Shift, kKeyCodeAltRight,
2354  static_cast<GdkModifierType>(GDK_MOD1_MASK |
2355  GDK_MODIFIER_RESERVED_13_MASK |
2356  GDK_MODIFIER_RESERVED_25_MASK));
2357  EXPECT_EQ(call_records->len, 7u);
2358  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 6));
2359  EXPECT_EQ(record->event->physical, 0u);
2360  EXPECT_EQ(record->event->logical, 0u);
2361 
2362  send_key_event(kRelease, GDK_KEY_Shift_L, kKeyCodeShiftLeft,
2363  static_cast<GdkModifierType>(GDK_MODIFIER_RESERVED_13_MASK |
2364  GDK_MODIFIER_RESERVED_25_MASK));
2365  EXPECT_EQ(call_records->len, 9u);
2366  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 7));
2367  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
2368  EXPECT_EQ(record->event->physical, kPhysicalAltLeft);
2369  EXPECT_EQ(record->event->logical, kLogicalAltLeft);
2370  EXPECT_EQ(record->event->synthesized, true);
2371 
2372  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 8));
2373  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeUp);
2374  EXPECT_EQ(record->event->physical, kPhysicalShiftLeft);
2375  EXPECT_EQ(record->event->logical, kLogicalGroupNext);
2376  EXPECT_EQ(record->event->synthesized, false);
2377 }
2378 
2379 // Shift + AltLeft results in GDK event whose keyval is MetaLeft but whose
2380 // keycode is either AltLeft or Shift keycode (depending on which one was
2381 // released last). The physical key is usually deduced from the keycode, but in
2382 // this case (Shift + AltLeft) a correction is needed otherwise the physical
2383 // key won't be the MetaLeft one.
2384 // Regression test for https://github.com/flutter/flutter/issues/96082
2385 TEST_F(FlKeyEmbedderResponderTest, HandlesShiftAltLeftIsMetaLeft) {
2386  StartEngine();
2387 
2388  g_autoptr(FlKeyEmbedderResponder) responder =
2390 
2391  g_autoptr(GPtrArray) call_records =
2392  g_ptr_array_new_with_free_func(g_object_unref);
2393  fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
2394  SendKeyEvent,
2395  ([&call_records](auto engine, const FlutterKeyEvent* event,
2396  FlutterKeyEventCallback callback, void* user_data) {
2397  g_ptr_array_add(call_records, fl_key_embedder_call_record_new(
2398  event, callback, user_data));
2399  callback(true, user_data);
2400  return kSuccess;
2401  }));
2402 
2403  guint32 now_time = 1;
2404  // A convenient shorthand to simulate events.
2405  auto send_key_event = [responder, &now_time](bool is_press, guint keyval,
2406  guint16 keycode,
2407  GdkModifierType state) {
2408  now_time += 1;
2409  g_autoptr(FlKeyEvent) event =
2410  fl_key_event_new(now_time, is_press, keycode, keyval, state, 0);
2411  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
2413  responder, event, 0, nullptr,
2414  [](GObject* object, GAsyncResult* result, gpointer user_data) {
2415  gboolean handled;
2417  FL_KEY_EMBEDDER_RESPONDER(object), result, &handled, nullptr));
2418  EXPECT_EQ(handled, TRUE);
2419 
2420  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
2421  },
2422  loop);
2423  g_main_loop_run(loop);
2424  };
2425 
2426  // ShiftLeft + AltLeft
2427  send_key_event(kPress, GDK_KEY_Shift_L, kKeyCodeShiftLeft,
2428  GDK_MODIFIER_RESERVED_25_MASK);
2429  EXPECT_EQ(call_records->len, 1u);
2430  FlKeyEmbedderCallRecord* record =
2431  FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
2432  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2433  EXPECT_EQ(record->event->physical, kPhysicalShiftLeft);
2434  EXPECT_EQ(record->event->logical, kLogicalShiftLeft);
2435  EXPECT_EQ(record->event->synthesized, false);
2436 
2437  send_key_event(kPress, GDK_KEY_Meta_L, kKeyCodeAltLeft,
2438  static_cast<GdkModifierType>(GDK_SHIFT_MASK |
2439  GDK_MODIFIER_RESERVED_25_MASK));
2440  EXPECT_EQ(call_records->len, 2u);
2441  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 1));
2442  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2443  EXPECT_EQ(record->event->physical, kPhysicalMetaLeft);
2444  EXPECT_EQ(record->event->logical, kLogicalMetaLeft);
2445  EXPECT_EQ(record->event->synthesized, false);
2446 
2447  send_key_event(kRelease, GDK_KEY_Meta_L, kKeyCodeAltLeft,
2448  static_cast<GdkModifierType>(GDK_MODIFIER_RESERVED_13_MASK |
2449  GDK_MODIFIER_RESERVED_25_MASK));
2450  send_key_event(kRelease, GDK_KEY_Shift_L, kKeyCodeShiftLeft,
2451  GDK_MODIFIER_RESERVED_25_MASK);
2452  clear_records(call_records);
2453 
2454  // ShiftRight + AltLeft
2455  send_key_event(kPress, GDK_KEY_Shift_R, kKeyCodeShiftRight,
2456  GDK_MODIFIER_RESERVED_25_MASK);
2457  EXPECT_EQ(call_records->len, 1u);
2458  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 0));
2459  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2460  EXPECT_EQ(record->event->physical, kPhysicalShiftRight);
2461  EXPECT_EQ(record->event->logical, kLogicalShiftRight);
2462  EXPECT_EQ(record->event->synthesized, false);
2463 
2464  send_key_event(kPress, GDK_KEY_Meta_L, kKeyCodeAltLeft,
2465  static_cast<GdkModifierType>(GDK_SHIFT_MASK |
2466  GDK_MODIFIER_RESERVED_25_MASK));
2467  EXPECT_EQ(call_records->len, 2u);
2468  record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(call_records, 1));
2469  EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
2470  EXPECT_EQ(record->event->physical, kPhysicalMetaLeft);
2471  EXPECT_EQ(record->event->logical, kLogicalMetaLeft);
2472  EXPECT_EQ(record->event->synthesized, false);
2473 }
g_autoptr(FlEngine) engine
const char FlTextDirection FlAssertiveness gpointer user_data
return TRUE
FlutterEngineProcTable * fl_engine_get_embedder_api(FlEngine *self)
Definition: fl_engine.cc:940
void fl_key_embedder_responder_handle_event(FlKeyEmbedderResponder *self, FlKeyEvent *event, uint64_t specified_logical_key, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
gboolean fl_key_embedder_responder_handle_event_finish(FlKeyEmbedderResponder *self, GAsyncResult *result, gboolean *handled, GError **error)
FlKeyEmbedderResponder * fl_key_embedder_responder_new(FlEngine *engine)
G_DEFINE_TYPE(FlKeyEmbedderCallRecord, fl_key_embedder_call_record, G_TYPE_OBJECT) static void fl_key_embedder_call_record_init(FlKeyEmbedderCallRecord *self)
TEST_F(FlKeyEmbedderResponderTest, SendKeyEvent)
static void invoke_record_callback(FlKeyEmbedderCallRecord *record, bool expected_handled)
static void clear_records(GPtrArray *array)
static FlKeyEmbedderCallRecord * fl_key_embedder_call_record_new(const FlutterKeyEvent *event, FlutterKeyEventCallback callback, gpointer user_data)
static void fl_key_embedder_call_record_dispose(GObject *object)
static void fl_key_embedder_call_record_class_init(FlKeyEmbedderCallRecordClass *klass)
G_DECLARE_FINAL_TYPE(FlKeyEmbedderCallRecord, fl_key_embedder_call_record, FL, KEY_EMBEDDER_CALL_RECORD, GObject)
FlKeyEvent * fl_key_event_new(guint32 time, gboolean is_press, guint16 keycode, guint keyval, GdkModifierType state, guint8 group)
Definition: fl_key_event.cc:34
constexpr guint16 kKeyCodeKeyA
static void send_key_event(FlTextInputHandler *handler, gint keyval, gint state=0)