| Flutter Linux Embedder |

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