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