Flutter Engine
 
Loading...
Searching...
No Matches
fl_keyboard_manager_test.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <cstring>
8#include <vector>
9
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
43
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
58static void call_record_free(CallRecord* record) {
59 g_free(record->event_character);
60 g_free(record);
61}
62
63static void call_record_respond(CallRecord* record, bool handled) {
64 if (record->callback != nullptr) {
65 record->callback(handled, record->callback_user_data);
66 }
67}
68
69namespace {
70using ::flutter::testing::keycodes::kLogicalAltLeft;
71using ::flutter::testing::keycodes::kLogicalBracketLeft;
72using ::flutter::testing::keycodes::kLogicalComma;
73using ::flutter::testing::keycodes::kLogicalControlLeft;
74using ::flutter::testing::keycodes::kLogicalDigit1;
75using ::flutter::testing::keycodes::kLogicalKeyA;
76using ::flutter::testing::keycodes::kLogicalKeyB;
77using ::flutter::testing::keycodes::kLogicalKeyM;
78using ::flutter::testing::keycodes::kLogicalKeyQ;
79using ::flutter::testing::keycodes::kLogicalMetaLeft;
80using ::flutter::testing::keycodes::kLogicalMinus;
81using ::flutter::testing::keycodes::kLogicalParenthesisRight;
82using ::flutter::testing::keycodes::kLogicalSemicolon;
83using ::flutter::testing::keycodes::kLogicalShiftLeft;
84using ::flutter::testing::keycodes::kLogicalUnderscore;
85
86using ::flutter::testing::keycodes::kPhysicalAltLeft;
87using ::flutter::testing::keycodes::kPhysicalControlLeft;
88using ::flutter::testing::keycodes::kPhysicalKeyA;
89using ::flutter::testing::keycodes::kPhysicalKeyB;
90using ::flutter::testing::keycodes::kPhysicalMetaLeft;
91using ::flutter::testing::keycodes::kPhysicalShiftLeft;
92
93constexpr guint16 kKeyCodeKeyA = 0x26u;
94constexpr guint16 kKeyCodeKeyM = 0x3au;
95constexpr guint16 kKeyCodeDigit1 = 0x0au;
96constexpr guint16 kKeyCodeMinus = 0x14u;
97constexpr guint16 kKeyCodeSemicolon = 0x2fu;
98constexpr 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..
105typedef std::array<uint32_t, 256> MockGroupLayoutData;
106typedef std::vector<const MockGroupLayoutData*> MockLayoutData;
107
108extern const MockLayoutData kLayoutRussian;
109extern const MockLayoutData kLayoutFrench;
110
111TEST(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.
117 messenger, "flutter/keyevent",
118 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
119 gpointer user_data) {
120 g_autoptr(FlValue) return_value = fl_value_new_map();
121 fl_value_set_string_take(return_value, "handled",
123 return fl_value_ref(return_value);
124 },
125 nullptr);
126
127 g_autoptr(FlEngine) engine =
128 fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
129 g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
130
131 EXPECT_TRUE(fl_engine_start(engine, nullptr));
132
133 // Don't handle first event - async call never completes.
135 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
137 void* user_data) { return kSuccess; }));
138 g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
139 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
140 gboolean first_event_completed = FALSE;
142 manager, event1, nullptr,
143 [](GObject* object, GAsyncResult* result, gpointer user_data) {
144 gboolean* first_event_completed = static_cast<gboolean*>(user_data);
145 *first_event_completed = TRUE;
146 },
147 &first_event_completed);
148
149 // Handle second event.
151 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
153 callback(true, user_data);
154 return kSuccess;
155 }));
156 g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
157 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
158 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
160 manager, event2, nullptr,
161 [](GObject* object, GAsyncResult* result, gpointer user_data) {
162 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
164 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
165 EXPECT_EQ(redispatched_event, nullptr);
166 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
167 },
168 loop);
169 g_main_loop_run(loop);
170
171 EXPECT_FALSE(first_event_completed);
172
173 // Passes if the cleanup does not crash.
174}
175
176TEST(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.
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);
199 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
201 callback(true, user_data);
202 return kSuccess;
203 }));
204
205 g_autoptr(FlKeyEvent) event = fl_key_event_new(
206 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
207 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
209 manager, event, nullptr,
210 [](GObject* object, GAsyncResult* result, gpointer user_data) {
211 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
213 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
214 EXPECT_EQ(redispatched_event, nullptr);
215 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
216 },
217 loop);
218 g_main_loop_run(loop);
219}
220
221TEST(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.
234 messenger, "flutter/keyevent",
235 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
236 gpointer user_data) {
237 g_autoptr(FlValue) return_value = fl_value_new_map();
238 fl_value_set_string_take(return_value, "handled",
240 return fl_value_ref(return_value);
241 },
242 nullptr);
244 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
246 callback(false, user_data);
247 return kSuccess;
248 }));
249
250 g_autoptr(FlKeyEvent) event = fl_key_event_new(
251 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
252 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
254 manager, event, nullptr,
255 [](GObject* object, GAsyncResult* result, gpointer user_data) {
256 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
258 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
259 EXPECT_EQ(redispatched_event, nullptr);
260 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
261 },
262 loop);
263 g_main_loop_run(loop);
264}
265
266TEST(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.
279 messenger, "flutter/keyevent",
280 [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
281 gpointer user_data) {
282 g_autoptr(FlValue) return_value = fl_value_new_map();
283 fl_value_set_string_take(return_value, "handled",
285 return fl_value_ref(return_value);
286 },
287 nullptr);
289 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
291 callback(true, user_data);
292 return kSuccess;
293 }));
294
295 g_autoptr(FlKeyEvent) event = fl_key_event_new(
296 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
297 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
299 manager, event, nullptr,
300 [](GObject* object, GAsyncResult* result, gpointer user_data) {
301 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
303 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
304 EXPECT_EQ(redispatched_event, nullptr);
305 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
306 },
307 loop);
308 g_main_loop_run(loop);
309}
310
311TEST(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.
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);
334 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
336 callback(false, user_data);
337 return kSuccess;
338 }));
339
340 g_autoptr(FlKeyEvent) event = fl_key_event_new(
341 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
342 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
344 manager, event, nullptr,
345 [](GObject* object, GAsyncResult* result, gpointer user_data) {
346 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
348 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
349 EXPECT_NE(redispatched_event, nullptr);
350 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
351 },
352 loop);
353 g_main_loop_run(loop);
354}
355
356static void channel_respond(FlMockBinaryMessenger* messenger,
357 GTask* task,
358 gboolean handled) {
360 fl_value_set_string_take(value, "handled", fl_value_new_bool(handled));
362}
363
364TEST(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);
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));
391 SendKeyEvent, ([&embedder_call_records](
392 auto engine, const FlutterKeyEvent* event,
394 g_ptr_array_add(embedder_call_records,
396 return kSuccess;
397 }));
398
399 g_autoptr(FlKeyEvent) event = fl_key_event_new(
400 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
401 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
403 manager, event, nullptr,
404 [](GObject* object, GAsyncResult* result, gpointer user_data) {
405 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
407 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
408 EXPECT_EQ(redispatched_event, nullptr);
409 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
410 },
411 loop);
412
413 EXPECT_EQ(embedder_call_records->len, 1u);
414 EXPECT_EQ(channel_calls->len, 1u);
415
417 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
418 true);
419 channel_respond(messenger,
420 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
421 FALSE);
422 g_main_loop_run(loop);
423}
424
425TEST(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);
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));
452 SendKeyEvent, ([&embedder_call_records](
453 auto engine, const FlutterKeyEvent* event,
455 g_ptr_array_add(embedder_call_records,
457 return kSuccess;
458 }));
459
460 g_autoptr(FlKeyEvent) event = fl_key_event_new(
461 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
462 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
464 manager, event, nullptr,
465 [](GObject* object, GAsyncResult* result, gpointer user_data) {
466 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
468 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
469 EXPECT_EQ(redispatched_event, nullptr);
470 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
471 },
472 loop);
473
474 EXPECT_EQ(embedder_call_records->len, 1u);
475 EXPECT_EQ(channel_calls->len, 1u);
476
478 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
479 false);
480 channel_respond(messenger,
481 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
482 TRUE);
483 g_main_loop_run(loop);
484}
485
486TEST(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);
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));
513 SendKeyEvent, ([&embedder_call_records](
514 auto engine, const FlutterKeyEvent* event,
516 g_ptr_array_add(embedder_call_records,
518 return kSuccess;
519 }));
520
521 g_autoptr(FlKeyEvent) event = fl_key_event_new(
522 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
523 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
525 manager, event, nullptr,
526 [](GObject* object, GAsyncResult* result, gpointer user_data) {
527 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
529 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
530 EXPECT_EQ(redispatched_event, nullptr);
531 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
532 },
533 loop);
534
535 EXPECT_EQ(embedder_call_records->len, 1u);
536 EXPECT_EQ(channel_calls->len, 1u);
537
539 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
540 true);
541 channel_respond(messenger,
542 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
543 TRUE);
544 g_main_loop_run(loop);
545}
546
547TEST(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);
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));
574 SendKeyEvent, ([&embedder_call_records](
575 auto engine, const FlutterKeyEvent* event,
577 g_ptr_array_add(embedder_call_records,
579 return kSuccess;
580 }));
581
582 g_autoptr(FlKeyEvent) event = fl_key_event_new(
583 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
584 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
586 manager, event, nullptr,
587 [](GObject* object, GAsyncResult* result, gpointer user_data) {
588 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
590 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
591 EXPECT_NE(redispatched_event, nullptr);
592 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
593 },
594 loop);
595
596 EXPECT_EQ(embedder_call_records->len, 1u);
597 EXPECT_EQ(channel_calls->len, 1u);
598
600 static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
601 false);
602 channel_respond(messenger,
603 static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
604 FALSE);
605 g_main_loop_run(loop);
606}
607
608TEST(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));
620 SendKeyEvent,
621 ([&call_records](auto engine, const FlutterKeyEvent* event,
623 g_ptr_array_add(call_records,
625 return kSuccess;
626 }));
627
628 auto sendTap = [&](guint8 keycode, guint keyval, guint8 group) {
629 g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
630 0, TRUE, keycode, keyval, static_cast<GdkModifierType>(0), group);
631 fl_keyboard_manager_handle_event(manager, event1, nullptr, nullptr,
632 nullptr);
633 g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
634 0, FALSE, keycode, keyval, static_cast<GdkModifierType>(0), group);
635 fl_keyboard_manager_handle_event(manager, event2, nullptr, nullptr,
636 nullptr);
637 };
638
639 /* US keyboard layout */
640
641 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
642 VERIFY_DOWN(kLogicalKeyA, "a");
643
644 sendTap(kKeyCodeKeyA, GDK_KEY_A, 0); // Shift-KeyA
645 VERIFY_DOWN(kLogicalKeyA, "A");
646
647 sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
648 VERIFY_DOWN(kLogicalDigit1, "1");
649
650 sendTap(kKeyCodeDigit1, GDK_KEY_exclam, 0); // Shift-Digit1
651 VERIFY_DOWN(kLogicalDigit1, "!");
652
653 sendTap(kKeyCodeMinus, GDK_KEY_minus, 0); // Minus
654 VERIFY_DOWN(kLogicalMinus, "-");
655
656 sendTap(kKeyCodeMinus, GDK_KEY_underscore, 0); // Shift-Minus
657 VERIFY_DOWN(kLogicalUnderscore, "_");
658
659 /* French keyboard layout, group 3, which is when the input method is showing
660 * "Fr" */
661
663 manager,
664 [](const GdkKeymapKey* key, gpointer user_data) {
665 MockLayoutData* layout_data = static_cast<MockLayoutData*>(user_data);
666 guint8 group = static_cast<guint8>(key->group);
667 EXPECT_LT(group, layout_data->size());
668 const MockGroupLayoutData* group_layout = (*layout_data)[group];
669 EXPECT_NE(group_layout, nullptr);
670 EXPECT_TRUE(key->level == 0 || key->level == 1);
671 bool shift = key->level == 1;
672 return (*group_layout)[key->keycode * 2 + shift];
673 },
674 reinterpret_cast<gpointer>(const_cast<MockLayoutData*>(&kLayoutFrench)));
675
676 sendTap(kKeyCodeKeyA, GDK_KEY_q, 3); // KeyA
677 VERIFY_DOWN(kLogicalKeyQ, "q");
678
679 sendTap(kKeyCodeKeyA, GDK_KEY_Q, 3); // Shift-KeyA
680 VERIFY_DOWN(kLogicalKeyQ, "Q");
681
682 sendTap(kKeyCodeSemicolon, GDK_KEY_m, 3); // ; but prints M
683 VERIFY_DOWN(kLogicalKeyM, "m");
684
685 sendTap(kKeyCodeKeyM, GDK_KEY_comma, 3); // M but prints ,
686 VERIFY_DOWN(kLogicalComma, ",");
687
688 sendTap(kKeyCodeDigit1, GDK_KEY_ampersand, 3); // Digit1
689 VERIFY_DOWN(kLogicalDigit1, "&");
690
691 sendTap(kKeyCodeDigit1, GDK_KEY_1, 3); // Shift-Digit1
692 VERIFY_DOWN(kLogicalDigit1, "1");
693
694 sendTap(kKeyCodeMinus, GDK_KEY_parenright, 3); // Minus
695 VERIFY_DOWN(kLogicalParenthesisRight, ")");
696
697 sendTap(kKeyCodeMinus, GDK_KEY_degree, 3); // Shift-Minus
698 VERIFY_DOWN(static_cast<uint32_t>(L'°'), "°");
699
700 /* French keyboard layout, group 0, which is pressing the "extra key for
701 * triggering input method" key once after switching to French IME. */
702
703 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
704 VERIFY_DOWN(kLogicalKeyA, "a");
705
706 sendTap(kKeyCodeDigit1, GDK_KEY_1, 0); // Digit1
707 VERIFY_DOWN(kLogicalDigit1, "1");
708
709 /* Russian keyboard layout, group 2 */
711 manager,
712 [](const GdkKeymapKey* key, gpointer user_data) {
713 MockLayoutData* layout_data = static_cast<MockLayoutData*>(user_data);
714 guint8 group = static_cast<guint8>(key->group);
715 EXPECT_LT(group, layout_data->size());
716 const MockGroupLayoutData* group_layout = (*layout_data)[group];
717 EXPECT_NE(group_layout, nullptr);
718 EXPECT_TRUE(key->level == 0 || key->level == 1);
719 bool shift = key->level == 1;
720 return (*group_layout)[key->keycode * 2 + shift];
721 },
722 reinterpret_cast<gpointer>(const_cast<MockLayoutData*>(&kLayoutRussian)));
723
724 sendTap(kKeyCodeKeyA, GDK_KEY_Cyrillic_ef, 2); // KeyA
725 VERIFY_DOWN(kLogicalKeyA, "Ñ„");
726
727 sendTap(kKeyCodeDigit1, GDK_KEY_1, 2); // Shift-Digit1
728 VERIFY_DOWN(kLogicalDigit1, "1");
729
730 sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_Cyrillic_ha, 2);
731 VERIFY_DOWN(kLogicalBracketLeft, "Ñ…");
732
733 /* Russian keyboard layout, group 0 */
734 sendTap(kKeyCodeKeyA, GDK_KEY_a, 0); // KeyA
735 VERIFY_DOWN(kLogicalKeyA, "a");
736
737 sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_bracketleft, 0);
738 VERIFY_DOWN(kLogicalBracketLeft, "[");
739}
740
741TEST(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));
753 SendKeyEvent,
754 ([&call_records](auto engine, const FlutterKeyEvent* event,
756 g_ptr_array_add(call_records,
758 return kSuccess;
759 }));
760
761 auto verifyModifierIsSynthesized = [&](GdkModifierType mask,
762 uint64_t physical, uint64_t logical) {
763 // Modifier is pressed.
764 guint state = mask;
766 EXPECT_EQ(call_records->len, 1u);
767 CallRecord* record =
768 static_cast<CallRecord*>(g_ptr_array_index(call_records, 0));
769 EXPECT_EQ(record->event_type, kFlutterKeyEventTypeDown);
770 EXPECT_EQ(record->event_physical, physical);
771 EXPECT_EQ(record->event_logical, logical);
772 EXPECT_STREQ(record->event_character, NULL);
773 EXPECT_EQ(record->event_synthesized, true);
774 // Modifier is released.
775 state = state ^ mask;
777 EXPECT_EQ(call_records->len, 2u);
778 record = static_cast<CallRecord*>(g_ptr_array_index(call_records, 1));
779 EXPECT_EQ(record->event_type, kFlutterKeyEventTypeUp);
780 EXPECT_EQ(record->event_physical, physical);
781 EXPECT_EQ(record->event_logical, logical);
782 EXPECT_STREQ(record->event_character, NULL);
783 EXPECT_EQ(record->event_synthesized, true);
784 g_ptr_array_set_size(call_records, 0);
785 };
786
787 // No modifiers pressed.
788 guint state = 0;
790 EXPECT_EQ(call_records->len, 0u);
791 g_ptr_array_set_size(call_records, 0);
792
793 // Press and release each modifier once.
794 verifyModifierIsSynthesized(GDK_CONTROL_MASK, kPhysicalControlLeft,
795 kLogicalControlLeft);
796 verifyModifierIsSynthesized(GDK_META_MASK, kPhysicalMetaLeft,
797 kLogicalMetaLeft);
798 verifyModifierIsSynthesized(GDK_MOD1_MASK, kPhysicalAltLeft, kLogicalAltLeft);
799 verifyModifierIsSynthesized(GDK_SHIFT_MASK, kPhysicalShiftLeft,
800 kLogicalShiftLeft);
801}
802
803TEST(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.
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);
824 SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
826 callback(false, user_data);
827 return kSuccess;
828 }));
829 g_autoptr(FlKeyEvent) event = fl_key_event_new(
830 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
831 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
833 manager, event, nullptr,
834 [](GObject* object, GAsyncResult* result, gpointer user_data) {
835 g_autoptr(FlKeyEvent) redispatched_event = nullptr;
837 FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
838 EXPECT_NE(redispatched_event, nullptr);
839 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
840 },
841 loop);
842 g_main_loop_run(loop);
843
844 GHashTable* pressed_state = fl_keyboard_manager_get_pressed_state(manager);
845 EXPECT_EQ(g_hash_table_size(pressed_state), 1u);
846
847 gpointer physical_key =
848 g_hash_table_lookup(pressed_state, uint64_to_gpointer(kPhysicalKeyA));
849 EXPECT_EQ(gpointer_to_uint64(physical_key), kLogicalKeyA);
850}
851
852// The following layout data is generated using DEBUG_PRINT_LAYOUT.
853
854const 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
890const 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
926const 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
962const 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
998const MockLayoutData kLayoutRussian{&kLayoutRussian0, nullptr,
999 &kLayoutRussian2};
1000const MockLayoutData kLayoutFrench{&kLayoutFrench0, nullptr, nullptr,
1001 &kLayoutFrench3};
1002
1003} // namespace
TEST(AsciiTableTest, Simple)
int32_t value
@ kSuccess
Definition embedder.h:73
void(* FlutterKeyEventCallback)(bool, void *)
Definition embedder.h:1427
FlutterKeyEventType
Definition embedder.h:1346
@ kFlutterKeyEventTypeDown
Definition embedder.h:1348
@ kFlutterKeyEventTypeUp
Definition embedder.h:1347
FlutterEngine engine
Definition main.cc:84
g_autoptr(GMutexLocker) locker
return TRUE
G_MODULE_EXPORT FlDartProject * fl_dart_project_new()
FlEngine * fl_engine_new_with_binary_messenger(FlBinaryMessenger *binary_messenger)
Definition fl_engine.cc:701
G_MODULE_EXPORT FlEngine * fl_engine_new(FlDartProject *project)
Definition fl_engine.cc:697
FlutterEngineProcTable * fl_engine_get_embedder_api(FlEngine *self)
Definition fl_engine.cc:868
gboolean fl_engine_start(FlEngine *self, GError **error)
Definition fl_engine.cc:726
FlKeyEvent * fl_key_event_new(guint32 time, gboolean is_press, guint16 keycode, guint keyval, GdkModifierType state, guint8 group)
constexpr guint16 kKeyCodeKeyA
void fl_keyboard_manager_handle_event(FlKeyboardManager *self, FlKeyEvent *event, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
GHashTable * fl_keyboard_manager_get_pressed_state(FlKeyboardManager *self)
gboolean fl_keyboard_manager_handle_event_finish(FlKeyboardManager *self, GAsyncResult *result, FlKeyEvent **redispatched_event, GError **error)
void fl_keyboard_manager_set_lookup_key_handler(FlKeyboardManager *self, FlKeyboardManagerLookupKeyHandler lookup_key_handler, gpointer user_data)
FlKeyboardManager * fl_keyboard_manager_new(FlEngine *engine)
void fl_keyboard_manager_sync_modifier_if_needed(FlKeyboardManager *self, guint state, double event_time)
static void call_record_respond(CallRecord *record, bool handled)
static void call_record_free(CallRecord *record)
#define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR)
static CallRecord * call_record_new(const FlutterKeyEvent *event, FlutterKeyEventCallback callback, void *callback_user_data)
FlMockBinaryMessenger * fl_mock_binary_messenger_new()
void fl_mock_binary_messenger_json_message_channel_respond(FlMockBinaryMessenger *self, GTask *task, FlValue *value)
void fl_mock_binary_messenger_set_json_message_channel(FlMockBinaryMessenger *self, const gchar *channel, FlMockBinaryMessengerMessageChannelHandler handler, gpointer user_data)
G_BEGIN_DECLS GBytes * message
G_MODULE_EXPORT FlValue * fl_value_new_map()
Definition fl_value.cc:366
G_MODULE_EXPORT FlValue * fl_value_ref(FlValue *self)
Definition fl_value.cc:394
G_MODULE_EXPORT void fl_value_set_string_take(FlValue *self, const gchar *key, FlValue *value)
Definition fl_value.cc:650
G_MODULE_EXPORT FlValue * fl_value_new_bool(bool value)
Definition fl_value.cc:255
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition fl_value.h:42
FlutterDesktopBinaryReply callback
gpointer uint64_to_gpointer(uint64_t number)
Definition key_mapping.h:17
uint64_t gpointer_to_uint64(gpointer pointer)
Definition key_mapping.h:13
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font manager
#define MOCK_ENGINE_PROC(proc, mock_impl)
FlutterKeyEventCallback callback
FlutterKeyEventType event_type
FlutterEngineSendKeyEventFnPtr SendKeyEvent
Definition embedder.h:3717
const char * character
Definition embedder.h:1409